form.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. <script lang="ts" setup>
  2. import type { ProductEntity } from '@vben/types';
  3. import { computed, ref } from 'vue';
  4. import { useVbenModal } from '@vben/common-ui';
  5. import { ElMessage } from 'element-plus';
  6. import { useVbenForm, z } from '#/adapter/form';
  7. import { getDictListApi } from '#/api/dict';
  8. import {
  9. addProductApi,
  10. editProductApi,
  11. getProductDetailApi,
  12. } from '#/api/product';
  13. import { getScqyListApi } from '#/api/scqy';
  14. import { getCustomerListApi } from '#/api/user';
  15. // 定义 props
  16. const props = defineProps<{
  17. defaultMerchantId?: string | string[];
  18. }>();
  19. const emit = defineEmits(['finish']);
  20. const data = ref();
  21. const formType = ref<'create' | 'detail' | 'edit'>('create');
  22. const channelOptions = ref<{ label: string; value: string }[]>([]);
  23. const scqyOptions = ref<{ label: string; value: string }[]>([]);
  24. const jjlxOptions = ref<{ label: string; value: string }[]>([]);
  25. const cplbOptions = ref<{ label: string; value: string }[]>([]);
  26. // 获取经销商列表
  27. const fetchChannelOptions = async () => {
  28. try {
  29. // 确保清空之前的选项
  30. channelOptions.value = [];
  31. const response = await getCustomerListApi({
  32. 'usersnature.value': '经销商',
  33. pageindex: 1,
  34. rows: 100,
  35. });
  36. // 从response.Data中获取数据
  37. if (response && response.Data && Array.isArray(response.Data)) {
  38. channelOptions.value = response.Data.map((item: any) => ({
  39. label: item.usersname || '未命名经销商',
  40. value: item.usersid || '',
  41. })).filter((item: any) => item.value);
  42. }
  43. } catch (error) {
  44. console.error('获取经销商列表失败', error);
  45. }
  46. };
  47. // 获取生产企业列表
  48. const fetchScqyOptions = async () => {
  49. try {
  50. // 确保清空之前的选项
  51. scqyOptions.value = [];
  52. const response = await getScqyListApi({
  53. pageindex: 1,
  54. rows: 100,
  55. });
  56. // 从response.Data中获取数据
  57. if (response && response.Data && Array.isArray(response.Data)) {
  58. scqyOptions.value = response.Data.map((item: any) => ({
  59. label: item.scqyinfomc || '未命名生产企业',
  60. value: item.scqyinfoid || '',
  61. })).filter((item: any) => item.value);
  62. }
  63. } catch (error) {
  64. console.error('获取生产企业列表失败', error);
  65. }
  66. };
  67. const fetchDictOptions = async () => {
  68. try {
  69. const [jjlxRes, cplbRes] = await Promise.all([
  70. getDictListApi({ groupname: 'product_jjlx', rows: 999, pageindex: 1 }),
  71. getDictListApi({ groupname: 'product_cplb', rows: 999, pageindex: 1 }),
  72. ]);
  73. jjlxOptions.value = (jjlxRes.Data || []).map((item: any) => ({
  74. label: item.title,
  75. value: item.substance,
  76. }));
  77. cplbOptions.value = (cplbRes.Data || []).map((item: any) => ({
  78. label: item.title,
  79. value: item.substance,
  80. }));
  81. } catch (error) {
  82. console.error('获取字典数据失败', error);
  83. }
  84. };
  85. const titleMap = {
  86. create: '新增产品',
  87. detail: '产品详情',
  88. edit: '编辑产品',
  89. } as const;
  90. const getTitle = computed(() => titleMap[formType.value]);
  91. // 判断是否有默认的经销商ID
  92. const hasDefaultMerchantId = computed(() => {
  93. return (
  94. props.defaultMerchantId &&
  95. (Array.isArray(props.defaultMerchantId)
  96. ? props.defaultMerchantId.length > 0
  97. : props.defaultMerchantId)
  98. );
  99. });
  100. const [BaseForm, baseFormApi] = useVbenForm({
  101. showDefaultActions: false,
  102. // 所有表单项共用,可单独在表单内覆盖
  103. commonConfig: {
  104. labelWidth: 140,
  105. // 所有表单项
  106. componentProps: {
  107. class: 'w-full',
  108. },
  109. },
  110. wrapperClass: 'grid-cols-1 lg:grid-cols-2',
  111. schema: [
  112. // { title: '产品名称', field: 'productsname' },
  113. // { title: '产品类别', field: 'productscategory' },
  114. // { title: '产品原价', field: 'productsprice' },
  115. // { title: '产品型号', field: 'productsmodel' },
  116. // { title: '产品sn号', field: 'productssn' },
  117. // { title: '品目', field: 'productspm' },
  118. // { title: '一级分类', field: 'productsfl1' },
  119. // { title: '二级分类', field: 'productsfl2' },
  120. // { title: '分档名称', field: 'productsfdmc' },
  121. // { title: '分档编号', field: 'productsfdbh' },
  122. // { title: '中央补贴金额', field: 'productszybt' },
  123. // { title: '特殊县中央补贴金额', field: 'productstsxzybt' },
  124. // { title: '机具类型', field: 'productsjjlx' },
  125. {
  126. component: 'Select',
  127. fieldName: 'productsjjlx',
  128. label: '机具类型',
  129. componentProps: {
  130. placeholder: '请选择机具类型',
  131. clearable: true,
  132. options: jjlxOptions,
  133. },
  134. rules: z.string().min(1, { message: '请选择机具类型' }),
  135. },
  136. {
  137. component: 'Input',
  138. fieldName: 'productsname',
  139. label: '产品名称',
  140. componentProps: {
  141. placeholder: '请输入产品名称',
  142. clearable: true,
  143. },
  144. rules: z.string().min(1, { message: '请输入产品名称' }),
  145. },
  146. {
  147. component: 'Select',
  148. fieldName: 'productscategory',
  149. label: '产品类别',
  150. componentProps: {
  151. placeholder: '请选择产品类别',
  152. clearable: true,
  153. options: cplbOptions,
  154. },
  155. rules: z.string().min(1, { message: '请选择产品类别' }),
  156. },
  157. {
  158. component: 'Select',
  159. fieldName: 'productsfl1',
  160. label: '一级分类',
  161. componentProps: {
  162. placeholder: '请选择一级分类',
  163. clearable: true,
  164. options: cplbOptions,
  165. },
  166. rules: z.string().min(1, { message: '请输入一级分类' }),
  167. },
  168. {
  169. component: 'Input',
  170. fieldName: 'productsmodel',
  171. label: '产品型号',
  172. componentProps: {
  173. placeholder: '请输入产品型号',
  174. clearable: true,
  175. },
  176. rules: z.string().min(1, { message: '请输入产品型号' }),
  177. },
  178. // {
  179. // component: 'Input',
  180. // fieldName: 'productssn',
  181. // label: '产品SN号',
  182. // componentProps: {
  183. // placeholder: '请输入产品SN号',
  184. // clearable: true,
  185. // },
  186. // rules: z.string().min(1, { message: '请输入产品SN号' }),
  187. // },
  188. // {
  189. // component: 'Input',
  190. // fieldName: 'productspm',
  191. // label: '品目',
  192. // componentProps: {
  193. // placeholder: '请输入品目',
  194. // clearable: true,
  195. // },
  196. // rules: z.string().min(1, { message: '请输入品目' }),
  197. // },
  198. {
  199. component: 'Select',
  200. fieldName: 'productsmerchantid',
  201. label: '关联经销商',
  202. componentProps: {
  203. placeholder: '请选择关联经销商',
  204. options: channelOptions,
  205. multiple: true,
  206. collapseTags: true,
  207. collapseTagsTooltip: true,
  208. clearable: true,
  209. filterable: true,
  210. noDataText: '暂无经销商数据',
  211. loading: false,
  212. style: { width: '100%' },
  213. },
  214. rules: z.array(z.string()).min(1, { message: '请选择关联经销商' }),
  215. // 使用dependencies控制字段显示
  216. dependencies: {
  217. triggerFields: ['productsname'], // 依赖产品名称字段来触发重新计算
  218. show: () => !hasDefaultMerchantId.value, // 当没有默认值时才显示该字段
  219. },
  220. },
  221. {
  222. component: 'Select',
  223. fieldName: 'productsscqyid',
  224. label: '关联生产企业',
  225. componentProps: {
  226. placeholder: '请选择关联生产企业',
  227. options: scqyOptions,
  228. clearable: true,
  229. filterable: true,
  230. noDataText: '暂无生产企业数据',
  231. loading: false,
  232. style: { width: '100%' },
  233. },
  234. rules: z.string().min(1, { message: '请选择关联生产企业' }),
  235. },
  236. // {
  237. // component: 'Input',
  238. // fieldName: 'productsfl2',
  239. // label: '二级分类',
  240. // componentProps: {
  241. // placeholder: '请输入二级分类',
  242. // clearable: true,
  243. // },
  244. // // rules: z.string().min(1, { message: '请输入二级分类' }),
  245. // },
  246. // {
  247. // component: 'Input',
  248. // fieldName: 'productsfdmc',
  249. // label: '分档名称',
  250. // componentProps: {
  251. // placeholder: '请输入分档名称',
  252. // clearable: true,
  253. // },
  254. // rules: z.string().min(1, { message: '请输入分档名称' }),
  255. // },
  256. // {
  257. // component: 'Input',
  258. // fieldName: 'productsfdbh',
  259. // label: '分档编号',
  260. // componentProps: {
  261. // placeholder: '请输入分档编号',
  262. // clearable: true,
  263. // },
  264. // rules: z.string().min(1, { message: '请输入分档编号' }),
  265. // },
  266. {
  267. component: 'InputNumber',
  268. fieldName: 'productsprice',
  269. label: '产品原价',
  270. componentProps: {
  271. placeholder: '请输入产品原价',
  272. min: 0,
  273. precision: 2,
  274. },
  275. rules: z.number().min(0, { message: '请输入正确的产品原价' }),
  276. },
  277. {
  278. component: 'InputNumber',
  279. fieldName: 'productszybt',
  280. label: '中央补贴',
  281. componentProps: {
  282. placeholder: '请输入中央补贴金额',
  283. min: 0,
  284. precision: 2,
  285. },
  286. rules: z.number().min(0, { message: '请输入正确的中央补贴金额' }),
  287. },
  288. {
  289. component: 'InputNumber',
  290. fieldName: 'productstsxzybt',
  291. label: '特殊县中央补贴',
  292. componentProps: {
  293. placeholder: '请输入特殊县中央补贴金额',
  294. min: 0,
  295. precision: 2,
  296. },
  297. rules: z.number().min(0, { message: '请输入正确的特殊县中央补贴金额' }),
  298. },
  299. ],
  300. });
  301. const [Modal, modalApi] = useVbenModal({
  302. class: 'w-7/12',
  303. onCancel() {
  304. modalApi.close();
  305. },
  306. async onConfirm() {
  307. // 校验输入的数据
  308. const validate = await baseFormApi.validate();
  309. if (!validate.valid) {
  310. return;
  311. }
  312. try {
  313. // 处理多选值的转换
  314. const formValues = { ...validate.values };
  315. if (Array.isArray(formValues.productsmerchantid)) {
  316. formValues.productsmerchantid = formValues.productsmerchantid.join(',');
  317. }
  318. // 调用新增或编辑接口
  319. const apiMap = {
  320. create: () => addProductApi(formValues as ProductEntity),
  321. edit: () =>
  322. editProductApi({
  323. ...formValues,
  324. 'productsid.value': data.value.row.productsid,
  325. } as any),
  326. };
  327. if (formType.value === 'detail') {
  328. modalApi.close();
  329. return;
  330. }
  331. await apiMap[formType.value]();
  332. ElMessage.success('操作成功');
  333. emit('finish');
  334. modalApi.close();
  335. } catch {}
  336. },
  337. async onOpenChange(isOpen) {
  338. if (isOpen) {
  339. data.value = modalApi.getData();
  340. formType.value = data.value.formType;
  341. baseFormApi.setState({
  342. commonConfig: { disabled: formType.value === 'detail' },
  343. });
  344. // 获取经销商列表和生产企业列表
  345. await Promise.all([
  346. fetchChannelOptions(),
  347. fetchScqyOptions(),
  348. fetchDictOptions(),
  349. ]);
  350. // 如果有默认的经销商ID,设置默认值
  351. if (hasDefaultMerchantId.value && data.value.formType === 'create') {
  352. // 设置默认值
  353. baseFormApi.setValues({
  354. productsmerchantid: '', // 设置为空的目的,是在接下来的经销商关联时,再去选择关联,而不是直接就进行关联
  355. });
  356. }
  357. if (data.value.formType === 'create') {
  358. return;
  359. }
  360. try {
  361. modalApi.setState({ loading: true });
  362. const detailData = await getProductDetailApi({
  363. productsid: data.value.row.productsid,
  364. });
  365. // 处理多选值的转换
  366. if (detailData.productsmerchantid) {
  367. detailData.productsmerchantid =
  368. detailData.productsmerchantid.split(',');
  369. }
  370. baseFormApi.setValues(detailData);
  371. } catch (error) {
  372. console.error('获取产品详情失败', error);
  373. }
  374. modalApi.setState({ loading: false });
  375. }
  376. },
  377. });
  378. // 暴露modalApi供外部调用
  379. defineExpose({
  380. modalApi,
  381. });
  382. </script>
  383. <template>
  384. <Modal :title="getTitle">
  385. <BaseForm />
  386. </Modal>
  387. </template>