InventoryStatistics.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <script setup lang="ts">
  2. import {
  3. Storehouse_Stock_List,
  4. Storehouse_Depot_List,
  5. Storehouse_ProductClass_List,
  6. Storehouse_Product_Model_List,
  7. Storehouse_Product_Name_List,
  8. Storehouse_Stock_Detail_List,
  9. Storehouse_Stock_Detail_Excel
  10. } from '@/api/storehouse/index'
  11. import { ref, reactive, onMounted } from 'vue'
  12. import { List, Picture, ArrowUpBold, ArrowDownBold } from '@element-plus/icons-vue'
  13. import Drawer from '@/components/Drawer/index.vue'
  14. import { GlobalStore } from '@/stores/index'
  15. import TableBase from '@/components/TableBase/index.vue'
  16. import type { ColumnProps } from '@/components/TableBase/interface/index'
  17. import { dayJs } from '@/utils/common'
  18. const globalStore = GlobalStore()
  19. const DrawerRef = ref<InstanceType<typeof Drawer> | null>(null)
  20. const TableRef = ref<InstanceType<typeof TableBase> | null>(null)
  21. const TableDetailRef = ref<InstanceType<typeof TableBase> | null>(null)
  22. let timeout: NodeJS.Timeout
  23. const searchShow = ref(false)
  24. const initParam = reactive({
  25. User_tokey: globalStore.GET_User_tokey,
  26. T_depot_id: '',
  27. T_product_class: '',
  28. T_product_name: '',
  29. T_product_model: ''
  30. })
  31. const detailInitParam = reactive({
  32. T_depot_id: '',
  33. T_product_id: '',
  34. T_start_date: '',
  35. T_end_date: ''
  36. })
  37. const columns: ColumnProps[] = [
  38. { type: 'index', label: '序号', width: 80 },
  39. { prop: 'T_depot_name', label: '仓库名称' },
  40. { prop: 'T_product_img', label: '产品图片', name: 'T_product_img' },
  41. { prop: 'T_product_name', label: '产品名称' },
  42. { prop: 'T_product_class_name', label: '产品分类' },
  43. { prop: 'T_product_model', label: '产品型号', ellipsis: true },
  44. { prop: 'T_product_spec', label: '产品规格' },
  45. { prop: 'T_total', label: '库存数量' },
  46. { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
  47. ]
  48. const detailColumns: ColumnProps[] = [
  49. { type: 'index', label: '序号', width: 80 },
  50. { prop: 'T_product_name', label: '产品名称' },
  51. { prop: 'T_product_model', label: '产品型号', ellipsis: true },
  52. { prop: 'T_product_spec', label: '产品规格' },
  53. { prop: 'T_month', label: '月份' },
  54. { prop: 'T_beginning', label: '期初库存' },
  55. { prop: 'T_in', label: '入库' },
  56. { prop: 'T_out', label: '出库' },
  57. { prop: 'T_ending', label: '期末库存' }
  58. ]
  59. const searchShowHandle = () => (searchShow.value = !searchShow.value)
  60. const searchHandle = () => {
  61. TableRef.value?.searchTable()
  62. }
  63. const dataCallback = (res: any) => {
  64. return res.Data.Data.map((item: any) => {
  65. depotOptions.value.forEach((depot: any) => {
  66. if (item.T_depot_id === depot.Id) {
  67. item.T_depot_name = depot.T_name
  68. }
  69. })
  70. return item
  71. })
  72. }
  73. const detailDataCallback = (res: any) => res.Data
  74. /**
  75. * 导出表格
  76. */
  77. const T_date = ref<string[]>([]) // 作为初始值
  78. const T_date_detail = ref<string[]>([])
  79. const T_date_export = ref<string[]>([])
  80. const visible = ref(false)
  81. const exportExcel = () => (visible.value = true)
  82. const confirmExpor = async () => {
  83. const params = {
  84. User_tokey: globalStore.GET_User_tokey,
  85. T_depot_id: initParam.T_depot_id,
  86. T_start_date: T_date.value[0],
  87. T_end_date: T_date.value[1]
  88. }
  89. const res = await Storehouse_Stock_Detail_Excel(params)
  90. console.log(res)
  91. }
  92. // 拿到仓库列表
  93. interface ItemType {
  94. T_name: string
  95. Id: number
  96. }
  97. const depotOptions = ref<ItemType[]>([])
  98. const classOptions = ref<any[]>([])
  99. const modelOptions = ref<any[]>([])
  100. // 获取产品分类
  101. const getProductClassList = async () => {
  102. const res: any = await Storehouse_ProductClass_List({ page: 1, page_z: 999 })
  103. classOptions.value = res.Data.Data
  104. }
  105. /**
  106. * 获取仓库列表
  107. */
  108. const getDepotList = async () => {
  109. if (globalStore.GET_depotList.length) {
  110. depotOptions.value = globalStore.GET_depotList
  111. return
  112. }
  113. const res: any = await Storehouse_Depot_List({ User_tokey: globalStore.GET_User_tokey, page: 1, page_z: 999 })
  114. depotOptions.value = res.Data.Data
  115. globalStore.SET_depotList(depotOptions.value)
  116. }
  117. /**
  118. * 模糊搜索名称
  119. * @param str 名称字符串
  120. */
  121. const getNameAsync = async (str: string): Promise<any> => {
  122. const res: any = await Storehouse_Product_Name_List({ T_name: str, T_class: initParam.T_product_class })
  123. if (!res.Data) return
  124. return res.Data.map((item: any, index: number) => {
  125. return {
  126. value: item,
  127. index: index
  128. }
  129. })
  130. }
  131. /**
  132. * 模糊搜索名称
  133. * @param queryString 输入框的输入字符
  134. * @param cb 异步搜索后的回调
  135. */
  136. const querySearchAsync = async (queryString: string, cb: (arg: any) => void) => {
  137. clearTimeout(timeout)
  138. globalStore.SET_isloading(true)
  139. timeout = setTimeout(async () => {
  140. const results = await getNameAsync(queryString)
  141. cb(results)
  142. globalStore.SET_isloading(false)
  143. }, 2000)
  144. }
  145. /**
  146. * 异步获取产品型号
  147. */
  148. const getProductModelList = async () => {
  149. globalStore.SET_isloading(true)
  150. const res: any = await Storehouse_Product_Model_List({ T_name: initParam.T_product_name })
  151. modelOptions.value = res.Data.map((item: any, index: number) => {
  152. return {
  153. value: item,
  154. index: index
  155. }
  156. })
  157. globalStore.SET_isloading(false)
  158. }
  159. const handleSelect = (item: any) => {
  160. initParam.T_product_name = item.value
  161. getProductModelList()
  162. }
  163. // 明细
  164. const callbackDrawer = (done: () => void) => done()
  165. const previewDetail = (id: string) => {
  166. DrawerRef.value?.openDrawer()
  167. T_date_detail.value = [...T_date.value]
  168. if (detailInitParam.T_product_id === id) return
  169. detailInitParam.T_depot_id = initParam.T_depot_id
  170. detailInitParam.T_product_id = id
  171. detailInitParam.T_start_date = T_date.value[0]
  172. detailInitParam.T_end_date = T_date.value[1]
  173. TableDetailRef.value?.searchTable()
  174. }
  175. const dateDetailChange = (str: string[]) => {
  176. if (!str) return
  177. detailInitParam.T_start_date = str[0]
  178. detailInitParam.T_end_date = str[1]
  179. TableDetailRef.value?.searchTable()
  180. }
  181. onMounted(() => {
  182. getDepotList()
  183. getProductClassList()
  184. T_date.value = [dayJs().startOf('year').format('YYYY-MM-DD'), dayJs().format('YYYY-MM-DD')]
  185. T_date_export.value = [...T_date.value]
  186. })
  187. </script>
  188. <template>
  189. <div class="inventory-statistics">
  190. <TableBase
  191. ref="TableRef"
  192. :columns="columns"
  193. :requestApi="Storehouse_Stock_List"
  194. :initParam="initParam"
  195. :dataCallback="dataCallback"
  196. >
  197. <template #table-header>
  198. <div class="head-search" :class="searchShow ? 'active' : ''">
  199. <el-form :model="initParam" class="result-form" label-width="100px">
  200. <el-row>
  201. <el-col :span="6"
  202. ><el-form-item label="产品分类" :inline-message="true">
  203. <el-select v-model="initParam.T_product_class" class="w-50 m-2" clearable placeholder="请选择分类~">
  204. <el-option v-for="item in classOptions" :key="item.Id" :label="item.T_name" :value="item.Id" />
  205. </el-select> </el-form-item
  206. ></el-col>
  207. <el-col :span="6"
  208. ><el-form-item label="产品名称">
  209. <el-autocomplete
  210. v-model="initParam.T_product_name"
  211. clearable
  212. :fetch-suggestions="querySearchAsync"
  213. placeholder="按产品名称搜索"
  214. :debounce="2000"
  215. :trigger-on-focus="false"
  216. @select="handleSelect"
  217. /> </el-form-item
  218. ></el-col>
  219. <el-col :span="6"
  220. ><el-form-item label="产品型号">
  221. <el-select v-model="initParam.T_product_model" clearable placeholder="请选择型号~">
  222. <el-option v-for="item in modelOptions" :key="item.index" :label="item.value" :value="item.value" />
  223. </el-select> </el-form-item
  224. ></el-col>
  225. <el-col :span="6">
  226. <el-button :icon="searchShow ? ArrowUpBold : ArrowDownBold" @click="searchShowHandle" />
  227. <el-button type="primary" @click="searchHandle">搜索</el-button>
  228. <el-popover :visible="visible" placement="bottom" :width="400" trigger="click">
  229. <template #reference>
  230. <el-button type="success" @click="exportExcel">导出</el-button>
  231. </template>
  232. <el-date-picker
  233. v-model="T_date_export"
  234. type="daterange"
  235. range-separator="~"
  236. start-placeholder="开始时间"
  237. end-placeholder="结束时间"
  238. format="YYYY-MM-DD"
  239. value-format="YYYY-MM-DD"
  240. />
  241. <div class="export-popover">
  242. <el-button size="small" @click="visible = false">取消</el-button>
  243. <el-button size="small" type="primary" @click="confirmExpor">确认</el-button>
  244. </div>
  245. </el-popover>
  246. </el-col>
  247. </el-row>
  248. <el-row class="search-bottom">
  249. <el-col :span="6"
  250. ><el-form-item label="仓库列表" :inline-message="true">
  251. <el-select v-model="initParam.T_depot_id" class="w-50 m-2" clearable placeholder="请选择分类~">
  252. <el-option v-for="item in depotOptions" :key="item.Id" :label="item.T_name" :value="item.Id" />
  253. </el-select> </el-form-item
  254. ></el-col>
  255. </el-row>
  256. </el-form>
  257. </div>
  258. </template>
  259. <template #T_product_img="{ row }">
  260. <el-image
  261. fit="cover"
  262. style="width: 50px; height: 50px"
  263. :src="row.T_product_img"
  264. :preview-src-list="[row.T_product_img]"
  265. :preview-teleported="true"
  266. >
  267. <template #error>
  268. <div class="image-slot">
  269. <el-icon><Picture /></el-icon>
  270. </div>
  271. </template>
  272. </el-image>
  273. </template>
  274. <template #right="{ row }">
  275. <el-button link type="primary" size="small" :icon="List" @click="previewDetail(row.T_product_id)"
  276. >明细</el-button
  277. >
  278. </template>
  279. </TableBase>
  280. <Drawer ref="DrawerRef" :handleClose="callbackDrawer" size="80%">
  281. <template #header="{ params }">
  282. <h4 :id="params.titleId" :class="params.titleClass">库存明细</h4>
  283. </template>
  284. <TableBase
  285. ref="TableDetailRef"
  286. :pagination="false"
  287. :columns="detailColumns"
  288. :requestApi="Storehouse_Stock_Detail_List"
  289. :initParam="detailInitParam"
  290. :dataCallback="detailDataCallback"
  291. >
  292. <template #table-header>
  293. <el-row class="head-search">
  294. <el-col :span="12"
  295. ><el-form-item label="产品名称">
  296. <el-date-picker
  297. v-model="T_date_detail"
  298. type="daterange"
  299. range-separator="~"
  300. start-placeholder="开始时间"
  301. end-placeholder="结束时间"
  302. format="YYYY-MM-DD"
  303. value-format="YYYY-MM-DD"
  304. @change="dateDetailChange" /></el-form-item
  305. ></el-col>
  306. </el-row>
  307. </template>
  308. </TableBase>
  309. </Drawer>
  310. </div>
  311. </template>
  312. <style scoped lang="scss">
  313. @import '@/styles/var.scss';
  314. .export-popover {
  315. padding: 5px;
  316. display: flex;
  317. justify-content: center;
  318. }
  319. :deep(.drawer__content) {
  320. @include f-direction;
  321. }
  322. .inventory-statistics {
  323. @include f-direction;
  324. .head-search {
  325. width: 100%;
  326. height: 33px;
  327. overflow: hidden;
  328. transition: all 0.5s ease-in-out;
  329. .result-form.el-form .el-form-item {
  330. margin-bottom: 0 !important;
  331. }
  332. .search-bottom {
  333. margin-top: 18px;
  334. }
  335. }
  336. .head-search.active {
  337. height: 85px;
  338. }
  339. .image-slot {
  340. display: flex;
  341. justify-content: center;
  342. align-items: center;
  343. width: 100%;
  344. height: 100%;
  345. background: var(--el-fill-color-light);
  346. color: var(--el-text-color-secondary);
  347. font-size: 30px;
  348. }
  349. .image-slot .el-icon {
  350. font-size: 30px;
  351. }
  352. }
  353. </style>