hook.tsx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import dayjs from "dayjs";
  2. import { message } from "@/utils/message";
  3. import { addDialog } from "@/components/ReDialog";
  4. import { usePublicHooks } from "@/views/system/hooks";
  5. import type { PaginationProps } from "@pureadmin/table";
  6. import {
  7. getNewProductApplyList,
  8. getNewProductApplyById,
  9. getNewProductApplyStatistics,
  10. submitNewProductApply,
  11. saveNewProductApplyDraft,
  12. updateNewProductApply,
  13. deleteNewProductApply,
  14. checkBarcode,
  15. refreshApplyStatus
  16. } from "@/api/newProductApply";
  17. import {
  18. ElForm,
  19. ElFormItem,
  20. ElInput,
  21. ElSelect,
  22. ElOption,
  23. ElInputNumber,
  24. ElMessageBox
  25. } from "element-plus";
  26. import {
  27. type Ref,
  28. h,
  29. ref,
  30. toRaw,
  31. reactive,
  32. defineComponent,
  33. onMounted
  34. } from "vue";
  35. export function useNewProductApply(tableRef: Ref) {
  36. const form = reactive({
  37. productName: "",
  38. barcode: "",
  39. status: "",
  40. applicantId: ""
  41. });
  42. const formRef = ref();
  43. const dataList = ref([]);
  44. const loading = ref(true);
  45. const statistics = ref({
  46. total: 0,
  47. pending: 0,
  48. approved: 0,
  49. rejected: 0
  50. });
  51. const { switchStyle } = usePublicHooks();
  52. const pagination = reactive<PaginationProps>({
  53. total: 0,
  54. pageSize: 10,
  55. currentPage: 1,
  56. background: true
  57. });
  58. const columns: TableColumnList = [
  59. {
  60. label: "申请编号",
  61. prop: "id",
  62. width: 90
  63. },
  64. {
  65. label: "商品名称",
  66. prop: "productName",
  67. minWidth: 150
  68. },
  69. {
  70. label: "条形码",
  71. prop: "barcode",
  72. minWidth: 130
  73. },
  74. {
  75. label: "分类",
  76. prop: "category",
  77. minWidth: 100
  78. },
  79. {
  80. label: "品牌",
  81. prop: "brand",
  82. minWidth: 100
  83. },
  84. {
  85. label: "售价(元)",
  86. prop: "price",
  87. minWidth: 100
  88. },
  89. {
  90. label: "申请人",
  91. prop: "applicantName",
  92. minWidth: 100
  93. },
  94. {
  95. label: "申请时间",
  96. prop: "applyTime",
  97. minWidth: 160,
  98. formatter: ({ applyTime }) =>
  99. applyTime ? dayjs(applyTime).format("YYYY-MM-DD HH:mm:ss") : ""
  100. },
  101. {
  102. label: "状态",
  103. prop: "status",
  104. minWidth: 100,
  105. cellRenderer: scope => {
  106. const statusMap = {
  107. 0: { text: "待提交", type: "info" },
  108. 1: { text: "待审核", type: "warning" },
  109. 2: { text: "已通过", type: "success" },
  110. 3: { text: "已拒绝", type: "danger" }
  111. };
  112. const status = statusMap[scope.row.status] || { text: "未知", type: "info" };
  113. return <el-tag type={status.type}>{status.text}</el-tag>;
  114. }
  115. },
  116. {
  117. label: "操作",
  118. fixed: "right",
  119. width: 200,
  120. slot: "operation"
  121. }
  122. ];
  123. async function onSearch() {
  124. loading.value = true;
  125. try {
  126. const searchParams: any = {
  127. page: pagination.currentPage,
  128. pageSize: pagination.pageSize
  129. };
  130. // 只添加非空的搜索条件
  131. if (form.productName) searchParams.productName = form.productName;
  132. if (form.barcode) searchParams.barcode = form.barcode;
  133. if (form.status) searchParams.status = form.status;
  134. if (form.applicantId) searchParams.applicantId = form.applicantId;
  135. const { data } = await getNewProductApplyList(searchParams);
  136. if (data) {
  137. dataList.value = data.list || [];
  138. pagination.total = Number(data.total) || 0;
  139. }
  140. } finally {
  141. loading.value = false;
  142. }
  143. }
  144. function resetForm(formEl) {
  145. if (!formEl) return;
  146. formEl.resetFields();
  147. onSearch();
  148. }
  149. async function getStatistics() {
  150. const { data } = await getNewProductApplyStatistics();
  151. if (data) {
  152. statistics.value = data;
  153. }
  154. }
  155. async function handleDelete(row) {
  156. const { code } = await deleteNewProductApply(row.id);
  157. if (code === 0) {
  158. message("删除成功", { type: "success" });
  159. onSearch();
  160. }
  161. }
  162. function openDialog(title = "新增", row?: any) {
  163. addDialog({
  164. title: `${title}新品申请`,
  165. props: {
  166. formInline: {
  167. id: row?.id,
  168. productName: row?.productName ?? "",
  169. barcode: row?.barcode ?? "",
  170. category: row?.category ?? "",
  171. brand: row?.brand ?? "",
  172. specification: row?.specification ?? "",
  173. unit: row?.unit ?? "",
  174. price: row?.price ?? 0,
  175. costPrice: row?.costPrice ?? 0,
  176. images: row?.images ?? "",
  177. description: row?.description ?? ""
  178. }
  179. },
  180. width: "600px",
  181. draggable: true,
  182. closeOnClickModal: false,
  183. contentRenderer: () => h(EditForm, { ref: formRef }),
  184. beforeSure: async (done) => {
  185. const FormRef = formRef.value.getRef();
  186. const curData = formRef.value.getForm();
  187. FormRef.validate(async (valid) => {
  188. if (valid) {
  189. let res;
  190. if (title === "新增") {
  191. res = await submitNewProductApply(curData);
  192. } else {
  193. res = await updateNewProductApply(curData.id, curData);
  194. }
  195. if (res.code === 0) {
  196. message(`${title}成功`, { type: "success" });
  197. done();
  198. onSearch();
  199. }
  200. }
  201. });
  202. }
  203. });
  204. }
  205. async function handleSaveDraft(row) {
  206. const { code } = await saveNewProductApplyDraft(row);
  207. if (code === 0) {
  208. message("草稿保存成功", { type: "success" });
  209. onSearch();
  210. }
  211. }
  212. async function handleCheckBarcode(barcode: string) {
  213. const { data } = await checkBarcode(barcode);
  214. if (data && data.exists) {
  215. message("该条码已存在商品库中", { type: "warning" });
  216. return data.product;
  217. }
  218. return null;
  219. }
  220. // 主动刷新审核状态
  221. async function handleRefreshStatus(row: any) {
  222. try {
  223. const { data, code } = await refreshApplyStatus(row.id);
  224. if (code === 0 && data) {
  225. message(`状态刷新成功`, { type: "success" });
  226. onSearch();
  227. getStatistics();
  228. } else {
  229. message("刷新失败", { type: "error" });
  230. }
  231. } catch {
  232. message("刷新失败", { type: "error" });
  233. }
  234. }
  235. function handleSizeChange(val: number) {
  236. pagination.pageSize = val;
  237. onSearch();
  238. }
  239. function handleCurrentChange(val: number) {
  240. pagination.currentPage = val;
  241. onSearch();
  242. }
  243. onMounted(() => {
  244. onSearch();
  245. getStatistics();
  246. });
  247. return {
  248. form,
  249. loading,
  250. columns,
  251. dataList,
  252. statistics,
  253. pagination,
  254. onSearch,
  255. resetForm,
  256. openDialog,
  257. handleDelete,
  258. handleSaveDraft,
  259. handleCheckBarcode,
  260. handleRefreshStatus,
  261. handleSizeChange,
  262. handleCurrentChange
  263. };
  264. }
  265. const EditForm = defineComponent({
  266. props: {
  267. formInline: {
  268. type: Object,
  269. default: () => ({})
  270. }
  271. },
  272. setup(props) {
  273. const ruleFormRef = ref();
  274. const model = reactive({
  275. id: props.formInline.id,
  276. productName: props.formInline.productName,
  277. barcode: props.formInline.barcode,
  278. category: props.formInline.category,
  279. brand: props.formInline.brand,
  280. specification: props.formInline.specification,
  281. unit: props.formInline.unit,
  282. price: props.formInline.price,
  283. costPrice: props.formInline.costPrice,
  284. images: props.formInline.images,
  285. description: props.formInline.description
  286. });
  287. const rules = {
  288. productName: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
  289. barcode: [{ required: true, message: "请输入条形码", trigger: "blur" }]
  290. };
  291. const getRef = () => ruleFormRef.value;
  292. const getForm = () => model;
  293. return () => (
  294. <ElForm ref={ruleFormRef} model={model} rules={rules} label-width="100px">
  295. <ElFormItem label="商品名称" prop="productName">
  296. <ElInput v-model={model.productName} placeholder="请输入商品名称" />
  297. </ElFormItem>
  298. <ElFormItem label="条形码" prop="barcode">
  299. <ElInput v-model={model.barcode} placeholder="请输入条形码" />
  300. </ElFormItem>
  301. <ElFormItem label="分类" prop="category">
  302. <ElInput v-model={model.category} placeholder="请输入分类" />
  303. </ElFormItem>
  304. <ElFormItem label="品牌" prop="brand">
  305. <ElInput v-model={model.brand} placeholder="请输入品牌" />
  306. </ElFormItem>
  307. <ElFormItem label="规格" prop="specification">
  308. <ElInput v-model={model.specification} placeholder="请输入规格" />
  309. </ElFormItem>
  310. <ElFormItem label="单位" prop="unit">
  311. <ElInput v-model={model.unit} placeholder="请输入单位" />
  312. </ElFormItem>
  313. <ElFormItem label="售价(元)" prop="price">
  314. <ElInputNumber v-model={model.price} precision={2} min={0} />
  315. </ElFormItem>
  316. <ElFormItem label="成本价(元)" prop="costPrice">
  317. <ElInputNumber v-model={model.costPrice} precision={2} min={0} />
  318. </ElFormItem>
  319. <ElFormItem label="商品描述" prop="description">
  320. <ElInput
  321. v-model={model.description}
  322. type="textarea"
  323. rows={3}
  324. placeholder="请输入商品描述"
  325. />
  326. </ElFormItem>
  327. </ElForm>
  328. );
  329. }
  330. });