releaseProduct.vue 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705
  1. <template>
  2. <view class="container">
  3. <view class="form-container">
  4. <up-form labelPosition="left" :model="formData" :rules="rules" ref="formRef" :labelWidth="100">
  5. <!-- 商品分类 -->
  6. <view class="card-title" v-if="!isProductCenter">商品分类</view>
  7. <up-form-item class="form-item" label="商品分类" prop="categoryIds" :borderBottom="false"
  8. @click="showCategory = true" :required="true" v-if="!isProductCenter">
  9. <up-input v-model="formData.categoryDisplayName" disabled disabledColor="#ffffff"
  10. placeholder="请选择商品分类" inputAlign="right" border="none"></up-input>
  11. <template #right>
  12. <up-icon name="arrow-right"></up-icon>
  13. </template>
  14. </up-form-item>
  15. <!-- 商品信息 -->
  16. <view class="card-title">商品信息</view>
  17. <up-form-item label="商品名称" prop="storeName" :borderBottom="false" :required="true">
  18. <up-input v-model="formData.storeName" placeholder="请输入商品名称" inputAlign="right"
  19. border="none"></up-input>
  20. </up-form-item>
  21. <up-form-item label="商品关键字" prop="keyword" :borderBottom="false" :required="true"
  22. v-if="!isProductCenter">
  23. <up-input v-model="formData.keyword" placeholder="请输入商品关键字" inputAlign="right"
  24. border="none"></up-input>
  25. </up-form-item>
  26. <up-form-item label="商品简介" prop="storeInfo" :borderBottom="false" :required="true"
  27. v-if="!isProductCenter">
  28. <up-textarea v-model="formData.storeInfo" placeholder="请输入商品简介" inputAlign="right"
  29. border="none"></up-textarea>
  30. </up-form-item>
  31. <up-form-item label="单位" prop="unitName" :borderBottom="false" :required="true" v-if="!isProductCenter">
  32. <up-input v-model="formData.unitName" placeholder="请输入单位" inputAlign="right"
  33. border="none"></up-input>
  34. </up-form-item>
  35. <!-- 图片上传 -->
  36. <view class="upload-section" v-if="!isProductCenter">
  37. <view class="upload-item">
  38. <view class="upload-label"><text class="required">*</text>商品封面图</view>
  39. <up-upload @afterRead="async (e) => {
  40. await afterRead(e);
  41. getImage();
  42. }" @delete="onPreviewImageDelete" name="preview" max-count="1" :fileList="previewImages">
  43. <view class="upload-btn">
  44. <up-icon name="plus" size="20" color="#ccc"></up-icon>
  45. <text class="upload-tip">点击上传</text>
  46. </view>
  47. </up-upload>
  48. <text class="format-tip">支持上传PNG、JPG格式的图片,每张不超过5MB。</text>
  49. </view>
  50. </view>
  51. <view class="upload-section" v-if="!isProductCenter">
  52. <view class="upload-item">
  53. <view class="upload-label"><text class="required">*</text>商品图片</view>
  54. <up-upload @afterRead="async (e) => {
  55. await afterRead(e);
  56. getImageProduct();
  57. }" @delete="onProductImageDelete" name="product" multiple :maxCount="10" :fileList="productImages">
  58. <view class="upload-btn">
  59. <up-icon name="plus" size="20" color="#ccc"></up-icon>
  60. <text class="upload-tip">点击上传</text>
  61. </view>
  62. </up-upload>
  63. <text class="format-tip">支持上传PNG、JPG格式图片,每张不超过5MB,最多可上传10张。</text>
  64. </view>
  65. </view>
  66. <!-- 商品属性 -->
  67. <view class="card-title">商品属性</view>
  68. <up-form-item label="材质" prop="metalType" :borderBottom="false" :required="true"
  69. v-if="!isProductCenter">
  70. <up-radio-group v-model="formData.metalType" placement="row">
  71. <up-radio :customStyle="{marginRight: '20rpx'}" v-for="(item, index) in materialList"
  72. :key="index" :label="item.name" :name="item.code" activeColor="#F8C008">
  73. </up-radio>
  74. </up-radio-group>
  75. </up-form-item>
  76. <up-form-item class="form-item" label="运费模板" prop="tempIds" :borderBottom="false"
  77. @click="showTemp = true" :required="true">
  78. <up-input v-model="formData.tempName" disabled disabledColor="#ffffff" placeholder="请选择运费模板"
  79. inputAlign="right" border="none"></up-input>
  80. <template #right>
  81. <up-icon name="arrow-right"></up-icon>
  82. </template>
  83. </up-form-item>
  84. <!-- 商品描述 -->
  85. <view class="editor-section" v-if="!isProductCenter">
  86. <view class="editor-header">
  87. <text class="editor-label">商品描述</text>
  88. <text class="word-count">{{ descriptionText.length }}/500</text>
  89. </view>
  90. <sp-editor editorId="editor" :toolbar-config="toolbarConfig" :readOnly="readOnly"
  91. @input="onEditorInput" @upinImage="onUpinImage" @init="onEditorInit"
  92. @overMax="onOverMax"></sp-editor>
  93. </view>
  94. <!-- 商品规格 -->
  95. <view class="pro-cate-container">
  96. <view class="cate-title" v-if="!isProductCenter">商品规格批量设置</view>
  97. <view class="cate-list">
  98. <view class="cate-item">
  99. <view class="cate-pic-line">
  100. <!-- <image src="@/static/avator.png" class="cate-pic" mode="widthFix"></image> -->
  101. <up-upload :fileList="oneTemplate.imgs" @afterRead="async (e) => {
  102. await afterRead(e);
  103. getImageProductGg('oneTemplate');
  104. }" @delete="onProductImageGgDelete('oneTemplate')" name="productGg" :maxCount="1">
  105. <view class="upload-btn">
  106. <up-icon name="plus" size="20" color="#666"></up-icon>
  107. <text class="upload-tip">上传图片</text>
  108. </view>
  109. </up-upload>
  110. <!-- <view class="cate-name">
  111. {{oneTemplate.attrName}}
  112. </view> -->
  113. </view>
  114. <view class="cate-ipt-line">
  115. <up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="oneTemplate.price"
  116. placeholder="请输入工费" inputAlign="left" border="none" type="digit" labelWidth="">
  117. <template #suffix>
  118. <text class="unit">元/g</text>
  119. </template>
  120. </up-input>
  121. <up-input class="cate-ipt" v-model="oneTemplate.stock" placeholder="请输入库存"
  122. inputAlign="left" border="none" type="number" labelWidth="">
  123. <template #suffix>
  124. <text class="unit">件</text>
  125. </template>
  126. </up-input>
  127. </view>
  128. <view class="cate-ipt-line">
  129. <up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="oneTemplate.weight"
  130. placeholder="请输入重量" inputAlign="left" border="none" type="digit" labelWidth="">
  131. <template #suffix>
  132. <text class="unit">g</text>
  133. </template>
  134. </up-input>
  135. <up-input class="cate-ipt" v-model="oneTemplate.additionalAmount" placeholder="请输入附加金额"
  136. inputAlign="left" border="none" type="number" labelWidth="">
  137. <template #suffix>
  138. <text class="unit">元</text>
  139. </template>
  140. </up-input>
  141. </view>
  142. <view class="cate-ipt-line" style="padding: 0;">
  143. <up-input class="cate-ipt" v-model="oneTemplate.barCode" placeholder="请输入商品编号"
  144. inputAlign="left" border="none" type="digit" labelWidth="">
  145. </up-input>
  146. </view>
  147. </view>
  148. </view>
  149. <view class="cate-add" @click="setAllAttr">
  150. 批量设置规格
  151. </view>
  152. </view>
  153. <view class="pro-cate-container">
  154. <view class="cate-title" v-if="!isProductCenter"><text style="color:red;">*</text>商品规格</view>
  155. <view class="cate-list">
  156. <view class="cate-item" v-for="(item,index) of attrTable" :key="'attr'+index">
  157. <view class="cate-pic-line">
  158. <!-- <image src="@/static/avator.png" class="cate-pic" mode="widthFix"></image> -->
  159. <up-upload :fileList="item.imgs" @afterRead="async (e) => {
  160. await afterRead(e);
  161. getImageProductGg(index);
  162. }" @delete="onProductImageGgDelete(index)" name="productGg" :maxCount="1">
  163. <view class="upload-btn">
  164. <up-icon name="plus" size="20" color="#666"></up-icon>
  165. <text class="upload-tip">上传图片</text>
  166. </view>
  167. </up-upload>
  168. <view class="cate-name">
  169. {{item.attrName}}
  170. </view>
  171. </view>
  172. <view class="cate-ipt-line">
  173. <up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="item.price"
  174. placeholder="请输入工费" inputAlign="left" border="none" type="digit" labelWidth="">
  175. <template #suffix>
  176. <text class="unit">元/g</text>
  177. </template>
  178. </up-input>
  179. <up-input class="cate-ipt" v-model="item.stock" placeholder="请输入库存" inputAlign="left"
  180. border="none" type="number" labelWidth="">
  181. <template #suffix>
  182. <text class="unit">件</text>
  183. </template>
  184. </up-input>
  185. </view>
  186. <view class="cate-ipt-line">
  187. <up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="item.weight"
  188. placeholder="请输入重量" inputAlign="left" border="none" type="digit" labelWidth="">
  189. <template #suffix>
  190. <text class="unit">g</text>
  191. </template>
  192. </up-input>
  193. <up-input class="cate-ipt" v-model="item.additionalAmount" placeholder="请输入附加金额"
  194. inputAlign="left" border="none" type="number" labelWidth="">
  195. <template #suffix>
  196. <text class="unit">元</text>
  197. </template>
  198. </up-input>
  199. </view>
  200. <view class="cate-ipt-line" style="padding: 0;">
  201. <up-input class="cate-ipt" v-model="item.barCode" placeholder="请输入商品编号"
  202. inputAlign="left" border="none" type="digit" labelWidth="">
  203. </up-input>
  204. </view>
  205. </view>
  206. </view>
  207. <view class="cate-add" @click="editCatePage">
  208. 管理规格
  209. </view>
  210. </view>
  211. <!-- <view class="card-title">商品规格</view>
  212. <up-form-item label="工费" prop="laborCost" :borderBottom="false" :required="true">
  213. <up-input v-model="formData.laborCost" placeholder="请输入工费" inputAlign="right" border="none"
  214. type="digit"></up-input>
  215. <template #right>
  216. <text class="unit">元/g</text>
  217. </template>
  218. </up-form-item>
  219. <up-form-item label="库存" prop="stock" :borderBottom="false" :required="true">
  220. <up-input v-model="formData.stock" placeholder="请输入库存" inputAlign="right" border="none"
  221. type="number"></up-input>
  222. </up-form-item>
  223. <up-form-item label="重量" prop="weight" :borderBottom="false" :required="true">
  224. <up-input v-model="formData.weight" placeholder="请输入重量" inputAlign="right" border="none"
  225. type="digit"></up-input>
  226. <template #right>
  227. <text class="unit">g</text>
  228. </template>
  229. </up-form-item>
  230. <up-form-item label="附加费" prop="additionalFee" :borderBottom="false" :required="true">
  231. <up-input v-model="formData.additionalFee" placeholder="请输入附加费" inputAlign="right" border="none"
  232. type="digit"></up-input>
  233. <template #right>
  234. <text class="unit">元</text>
  235. </template>
  236. </up-form-item>
  237. <up-form-item label="商品编号" prop="barCode" :borderBottom="false" :required="true">
  238. <up-input v-model="formData.barCode" placeholder="请输入商品编号" inputAlign="right" border="none"
  239. type="number"></up-input>
  240. </up-form-item>
  241. <view class="upload-section">
  242. <view class="upload-item">
  243. <view class="upload-label"><text class="required">*</text>规格图片</view>
  244. <up-upload :fileList="productImagesGg" @afterRead="async (e) => {
  245. await afterRead(e);
  246. getImageProductGg();
  247. }" @delete="onProductImageGgDelete" name="productGg" :maxCount="1">
  248. <view class="upload-btn">
  249. <up-icon name="plus" size="20" color="#ccc"></up-icon>
  250. <text class="upload-tip">点击上传</text>
  251. </view>
  252. </up-upload>
  253. <text class="format-tip">支持上传PNG、JPG格式图片,每张不超过5MB,最多可上传1张。</text>
  254. </view>
  255. </view> -->
  256. <up-form-item label="商品排序" prop="sort" :borderBottom="false">
  257. <up-input v-model="formData.sort" placeholder="请输入排序号" inputAlign="right" border="none"
  258. type="number"></up-input>
  259. </up-form-item>
  260. </up-form>
  261. </view>
  262. <!-- 发布按钮 -->
  263. <view class="btn-view">
  264. <button class="submit" @click="submitForm">立即发布</button>
  265. </view>
  266. <!-- 类目选择弹窗 -->
  267. <up-popup :show="showCategory" @close="showCategory = false" mode="bottom" round="20" :closeable="true">
  268. <view class="popup-content">
  269. <view class="popup-header">
  270. <text class="popup-title">选择商品分类</text>
  271. </view>
  272. <category-selector :categoryList="categoryData" :selectedIds="formData.categoryIds"
  273. @change="onCategoryChange" ref="categoryRef" />
  274. <view class="popup-actions">
  275. <button class="action-btn cancel" @click="showCategory = false">取消</button>
  276. <button class="action-btn confirm" @click="confirmCategory">确定</button>
  277. </view>
  278. </view>
  279. </up-popup>
  280. <!-- 运费模板-->
  281. <up-picker :show="showTemp" v-model="formData.tempIds" :columns="tempColumns" keyName="name" valueName="id"
  282. confirmColor="#F8C008" @close="showTemp = false" @confirm="tempConfirm"
  283. @cancel="showTemp = false"></up-picker>
  284. </view>
  285. </template>
  286. <script setup>
  287. import {
  288. ref,
  289. computed,
  290. watch,
  291. nextTick
  292. } from 'vue';
  293. import {
  294. onShow,
  295. onLoad,
  296. onUnload
  297. } from "@dcloudio/uni-app";
  298. import {
  299. productCategory,
  300. productSave,
  301. productUpdate,
  302. templatesList,
  303. productInfo
  304. } from "@/api/merchant";
  305. import CategorySelector from '@/components/CategorySelector';
  306. import {
  307. useAppStore
  308. } from "@/stores/app";
  309. import {
  310. useImageUpload
  311. } from "@/hooks/useImageUpload";
  312. import {
  313. useToast
  314. } from "@/hooks/useToast";
  315. const {
  316. Toast
  317. } = useToast();
  318. const {
  319. imageList,
  320. afterRead,
  321. deletePic,
  322. uploadLoading
  323. } = useImageUpload({
  324. pid: 1,
  325. model: "product",
  326. });
  327. const appStore = useAppStore();
  328. const merchantInfo = appStore.userInfo.merchant
  329. // 表单验证和提交
  330. const formRef = ref(null);
  331. // 表单数据
  332. const formData = ref({
  333. categoryIds: [], // 选中的分类ID数组
  334. categoryDisplayName: '', // 显示的分类名称
  335. tempName: '', // 显示的运费模板名称
  336. tempIds: [],
  337. storeName: '',
  338. keyword: '',
  339. storeInfo: '',
  340. unitName: '',
  341. metalType: '',
  342. weight: '',
  343. laborCost: '',
  344. additionalFee: '',
  345. sort: '',
  346. content: '',
  347. stock: '',
  348. barCode: ''
  349. });
  350. // 编辑器相关
  351. const editorIns = ref(null);
  352. const readOnly = ref(false);
  353. const descriptionText = ref('');
  354. const toolbarConfig = ref({
  355. iconSize: '20px',
  356. iconColumns: 10,
  357. excludeKeys: ['direction', 'date', 'lineHeight', 'letterSpacing', 'listCheck']
  358. });
  359. // 分类相关
  360. const showCategory = ref(false);
  361. const categoryData = ref([]);
  362. const categoryRef = ref();
  363. const showTemp = ref(false);
  364. const tempColumns = ref([])
  365. // 图片列表
  366. const previewImages = ref([]);
  367. const productImages = ref([]);
  368. const productImagesGg = ref([]);
  369. const productId = ref(null);
  370. const isProductCenter = ref(null);
  371. // 规格
  372. const attr = ref([]);
  373. // 规格列表
  374. const attrTable = ref([]);
  375. // 规格数据模板
  376. const oneTemplate = ref({
  377. image: '',
  378. imgs: [],
  379. price: '',
  380. additionalAmount: '',
  381. stock: '',
  382. barCode: '',
  383. weight: '',
  384. })
  385. // 规格历史数据,防止刷新数据时已填数据丢失
  386. let historyAttr = []
  387. // 规格表历史数据
  388. let historyTable = {}
  389. // 材质列表
  390. const materialList = ref([{
  391. name: '黄金',
  392. code: 'au'
  393. },
  394. {
  395. name: '铂金',
  396. code: 'pt'
  397. },
  398. {
  399. name: '白银',
  400. code: 'ag'
  401. }
  402. ]);
  403. // 验证规则
  404. const rules = ref({
  405. categoryIds: {
  406. type: 'array',
  407. required: true,
  408. message: '请选择商品分类',
  409. trigger: ['blur', 'change']
  410. },
  411. storeName: {
  412. type: 'string',
  413. required: true,
  414. message: '请输入商品名称',
  415. trigger: ['blur', 'change']
  416. },
  417. keyword: {
  418. type: 'string',
  419. required: true,
  420. message: '请输入商品关键字',
  421. trigger: ['blur', 'change']
  422. },
  423. storeInfo: {
  424. type: 'string',
  425. required: true,
  426. message: '请输入商品关键字',
  427. trigger: ['blur', 'change']
  428. },
  429. unitName: {
  430. type: 'string',
  431. required: true,
  432. message: '请输入单位',
  433. trigger: ['blur', 'change']
  434. },
  435. tempIds: {
  436. type: 'array',
  437. required: true,
  438. message: '请选择运费模板',
  439. trigger: ['blur', 'change']
  440. },
  441. metalType: {
  442. type: 'string',
  443. required: true,
  444. message: '请选择材质',
  445. trigger: ['blur', 'change']
  446. },
  447. sort: {
  448. type: 'string',
  449. pattern: /^\d*$/,
  450. message: '排序号必须是整数',
  451. trigger: ['blur', 'change'],
  452. validator: (rule, value) => {
  453. if (!value || value.trim().length === 0) return true; // 可选字段,为空时通过
  454. return /^\d+$/.test(value);
  455. }
  456. },
  457. // additionalFee: {
  458. // type: 'string',
  459. // required: true,
  460. // pattern: /^\d+(\.\d+)?$/,
  461. // message: '附加费必须是数字,可以是小数',
  462. // trigger: ['blur', 'change']
  463. // },
  464. // weight: {
  465. // type: 'string',
  466. // required: true,
  467. // pattern: /^\d+(\.\d+)?$/,
  468. // message: '重量必须是数字,可以是小数',
  469. // trigger: ['blur', 'change']
  470. // },
  471. // laborCost: {
  472. // type: 'string',
  473. // required: true,
  474. // pattern: /^\d+(\.\d+)?$/,
  475. // message: '工费必须是数字,可以是小数',
  476. // trigger: ['blur', 'change']
  477. // },
  478. // stock: {
  479. // type: 'string',
  480. // required: true,
  481. // pattern: /^\d*$/,
  482. // message: '库存必须是整数',
  483. // trigger: ['blur', 'change'],
  484. // validator: (rule, value) => {
  485. // if (!value || value.trim().length === 0) return false;
  486. // return /^\d+$/.test(value);
  487. // }
  488. // },
  489. // barCode: {
  490. // type: 'string',
  491. // required: true,
  492. // pattern: /^\d*$/,
  493. // message: '商品编号必须是整数',
  494. // trigger: ['blur', 'change'],
  495. // validator: (rule, value) => {
  496. // if (!value || value.trim().length === 0) return false;
  497. // return /^\d+$/.test(value);
  498. // }
  499. // },
  500. });
  501. // 页面加载
  502. onShow(() => {
  503. })
  504. onLoad(async (options) => {
  505. appStore.SET_CPATTR([])
  506. appStore.SET_CPATTRVALUE([])
  507. await getProductCategory();
  508. await getTempData();
  509. console.log(options)
  510. if (options.id) {
  511. productId.value = options.id;
  512. isProductCenter.value = options.isProductCenter || null;
  513. await getProductDetail(options.id);
  514. } else {
  515. resetForm();
  516. }
  517. uni.$on("updateAttr", onUpdateAttr)
  518. })
  519. onUnload(() => {
  520. uni.$off("updateAttr")
  521. })
  522. // 规格设置页面点击确认,更新当前页规格表格
  523. const onUpdateAttr = (data) => {
  524. // console.log("onUpdateAttr", data)
  525. const table = attrFormat(data)
  526. attr.value = data.concat();
  527. attrTable.value = table
  528. // console.log("table==>", table)
  529. }
  530. // 获取商品分类
  531. async function getProductCategory() {
  532. let obj = {
  533. type: 1,
  534. status: 1
  535. }
  536. try {
  537. const {
  538. data
  539. } = await productCategory(obj)
  540. console.log('原始分类数据:', data);
  541. const newArr = []
  542. data.forEach((value, index) => {
  543. newArr[index] = value
  544. if (value.child) newArr[index].child = value.child.filter(item => item.status === true)
  545. })
  546. // 过滤商品分类设置为隐藏的子分类不出现在树形列表里
  547. categoryData.value = newArr.filter(item => item.code !== 'bb_mall')
  548. console.log('转换后的分类数据:', categoryData.value);
  549. } catch (error) {
  550. console.error('获取商品分类失败:', error);
  551. uni.showToast({
  552. title: '获取分类失败',
  553. icon: 'none'
  554. });
  555. }
  556. }
  557. // 获取运费模板
  558. async function getTempData() {
  559. let obj = {
  560. page: 1,
  561. limit: 9999
  562. }
  563. try {
  564. const {
  565. data
  566. } = await templatesList()
  567. tempColumns.value[0] = data.list;
  568. } catch (error) {
  569. console.error('获取商品分类失败:', error);
  570. uni.showToast({
  571. title: '获取分类失败',
  572. icon: 'none'
  573. });
  574. }
  575. }
  576. const initFormValidation = async () => {
  577. await nextTick();
  578. if (!formRef.value) {
  579. console.error('表单引用不存在');
  580. return;
  581. }
  582. try {
  583. // 方法1:清除所有验证状态,然后重新验证
  584. formRef.value.clearValidate();
  585. // 方法2:延迟触发字段验证
  586. setTimeout(async () => {
  587. // 逐个触发必填字段的验证
  588. // const requiredFields = ['categoryIds', 'storeName', 'keyword', 'storeInfo', 'unitName',
  589. // 'tempIds', 'metalType', 'weight', 'laborCost', 'additionalFee',
  590. // 'stock', 'barCode'
  591. // ];
  592. const requiredFields = ['categoryIds', 'storeName', 'keyword', 'storeInfo', 'unitName',
  593. 'tempIds', 'metalType'
  594. ];
  595. for (const field of requiredFields) {
  596. try {
  597. await formRef.value.validateField(field);
  598. } catch (error) {
  599. console.log(`⚠️ 字段 ${field} 验证状态:`, error);
  600. }
  601. }
  602. }, 300);
  603. } catch (error) {
  604. console.error('初始化表单验证状态失败:', error);
  605. }
  606. };
  607. // 获取商品详情
  608. async function getProductDetail(id) {
  609. try {
  610. const {
  611. data
  612. } = await productInfo(id)
  613. console.log('获取商品详情:', data);
  614. // 使用Object.assign确保响应式更新
  615. Object.assign(formData.value, {
  616. ...data,
  617. // 确保categoryIds是数组格式
  618. categoryIds: data.cateId ? (Array.isArray(data.cateId) ? data.cateId : data.cateId.split(
  619. ',')) : [],
  620. // 确保tempIds是数组格式
  621. tempIds: data.tempId ? [data.tempId] : []
  622. });
  623. productImages.value = [];
  624. console.log('分类IDs:', formData.value.categoryIds);
  625. console.log('运费模板IDs:', formData.value.tempIds);
  626. // 等待DOM更新
  627. await nextTick();
  628. // 更新显示名称
  629. formData.value.categoryDisplayName = getCategoryDisplayName(formData.value.categoryIds);
  630. formData.value.tempName = formatterTemp(data.tempId);
  631. console.log('分类显示名称:', formData.value.categoryDisplayName);
  632. // 图片处理
  633. previewImages.value = data.image ? [{
  634. url: data.image
  635. }] : [];
  636. // 商品轮播图
  637. if (data.sliderImage) {
  638. try {
  639. const urlArr = typeof data.sliderImage === 'string' ? JSON.parse(data.sliderImage) : data
  640. .sliderImage;
  641. if (urlArr && urlArr.length > 0) {
  642. productImages.value = urlArr.map(url => ({
  643. url
  644. }));
  645. }
  646. } catch (error) {
  647. console.error('解析轮播图失败:', error);
  648. productImages.value = [];
  649. }
  650. }
  651. // 商品属性
  652. if (data.attrValue && data.attrValue.length > 0) {
  653. data.attr.forEach(item => {
  654. attr.value.push({
  655. attrName: item.attrName,
  656. attrValue: item.attrValues.split(',')
  657. })
  658. })
  659. data.attrValue.forEach(item => {
  660. let attrName = item.suk;
  661. attrTable.value.push({
  662. ...item,
  663. attrName: attrName,
  664. imgs: item?.image ? [{
  665. url: item.image
  666. }] : []
  667. })
  668. })
  669. appStore.SET_CPATTR(attr.value)
  670. }
  671. // 商品属性
  672. // if (data.attrValue && data.attrValue.length > 0) {
  673. // formData.value.additionalFee = data.attrValue[0].additionalAmount;
  674. // formData.value.laborCost = data.attrValue[0].price;
  675. // formData.value.barCode = data.attrValue[0].barCode;
  676. // formData.value.stock = data.attrValue[0].stock;
  677. // formData.value.weight = data.attrValue[0].weight;
  678. // // 规格图片
  679. // productImagesGg.value = data.attrValue[0].image ? [{
  680. // url: data.attrValue[0].image
  681. // }] : [];
  682. // }
  683. // 商品描述
  684. descriptionText.value = data.content ? data.content.replace(/<[^>]*>/g, '').substring(0, 500) : '';
  685. formData.value.content = data.content || '';
  686. // 设置分类选择器
  687. if (categoryRef.value && formData.value.categoryIds.length > 0) {
  688. // 使用nextTick确保组件已渲染
  689. await nextTick();
  690. if (categoryRef.value.setSelectedIds) {
  691. categoryRef.value.setSelectedIds(formData.value.categoryIds);
  692. }
  693. }
  694. // 重要:手动初始化表单验证状态
  695. await initFormValidation();
  696. } catch (error) {
  697. console.error('获取商品详情失败:', error);
  698. uni.showToast({
  699. title: '获取商品详情失败',
  700. icon: 'none'
  701. });
  702. }
  703. }
  704. // 运费模板id获取中文名
  705. function formatterTemp(id) {
  706. // 假设 tempColumns 是一个数组
  707. const foundItem = tempColumns.value[0].find(item => item.id == id);
  708. return foundItem ? foundItem.name : '';
  709. }
  710. function tempConfirm(obj) {
  711. formData.value.tempId = obj.value[0].id;
  712. formData.value.tempName = obj.value[0].name;
  713. showTemp.value = false;
  714. setTimeout(() => {
  715. if (formRef.value) {
  716. formRef.value.validateField('tempIds');
  717. }
  718. }, 100);
  719. }
  720. // 分类选择变化
  721. const onCategoryChange = (result) => {
  722. console.log('分类选择变化:', result);
  723. // 这里只更新显示,不直接更新表单数据,等用户点击确定
  724. }
  725. // 确认分类选择
  726. const confirmCategory = () => {
  727. if (categoryRef.value) {
  728. const selectedIds = categoryRef.value.getSelectedIds()
  729. if (selectedIds.length === 0) {
  730. uni.showToast({
  731. title: '请至少选择一个分类',
  732. icon: 'none'
  733. })
  734. return
  735. }
  736. // 更新表单数据
  737. formData.value.categoryIds = selectedIds
  738. formData.value.categoryDisplayName = getCategoryDisplayName(selectedIds)
  739. showCategory.value = false
  740. console.log('最终选中的分类ID:', selectedIds)
  741. // 触发校验
  742. setTimeout(() => {
  743. if (formRef.value) {
  744. formRef.value.validateField('categoryIds');
  745. }
  746. }, 100);
  747. }
  748. }
  749. // 根据选中的ID生成显示名称
  750. const getCategoryDisplayName = (selectedIds) => {
  751. if (!selectedIds || selectedIds.length === 0) return ''
  752. const names = []
  753. selectedIds.forEach(id => {
  754. // 检查是否是一级分类(表示全选)
  755. const firstLevel = categoryData.value.find(item => item.id == id)
  756. if (firstLevel) {
  757. // 如果是一级分类,显示"分类名称(全部)"
  758. names.push(`${firstLevel.name}`)
  759. } else {
  760. // 查找二级分类
  761. for (const parent of categoryData.value) {
  762. if (parent.child) {
  763. const secondLevel = parent.child.find(child => child.id == id)
  764. if (secondLevel) {
  765. names.push(`${parent.name}-${secondLevel.name}`)
  766. break
  767. }
  768. }
  769. }
  770. // 如果没有子分类的一级分类
  771. const singleLevel = categoryData.value.find(item =>
  772. !item.child && item.id == id
  773. )
  774. if (singleLevel) {
  775. names.push(singleLevel.name)
  776. }
  777. }
  778. })
  779. return names.join('、')
  780. }
  781. async function getImage() {
  782. console.log(imageList.value)
  783. if (imageList.value.length > 0) {
  784. if (imageList.value[0].status == "success") {
  785. previewImages.value = imageList.value;
  786. console.log('previewImages.value', previewImages.value)
  787. // change();
  788. } else {
  789. Toast({
  790. title: "上传失败"
  791. });
  792. }
  793. }
  794. imageList.value = [];
  795. }
  796. async function getImageProduct() {
  797. if (imageList.value.length > 0) {
  798. if (imageList.value[0].status == "success") {
  799. productImages.value = [...productImages.value, ...imageList.value];
  800. // change();
  801. } else {
  802. Toast({
  803. title: "上传失败"
  804. });
  805. }
  806. }
  807. imageList.value = [];
  808. }
  809. async function getImageProductGg(index) {
  810. if (imageList.value.length > 0) {
  811. if (imageList.value[0].status == "success") {
  812. console.log(imageList.value)
  813. if (index == "oneTemplate") {
  814. oneTemplate.value.imgs = imageList.value
  815. oneTemplate.value.image = imageList.value[0].info.url
  816. } else {
  817. attrTable.value[index].imgs = imageList.value
  818. attrTable.value[index].image = imageList.value[0].info.url
  819. }
  820. // productImagesGg.value = imageList.value;
  821. // change();
  822. } else {
  823. Toast({
  824. title: "上传失败"
  825. });
  826. }
  827. }
  828. imageList.value = [];
  829. }
  830. const onPreviewImageDelete = (e) => {
  831. previewImages.value.splice(e.index, 1);
  832. };
  833. const onProductImageDelete = (e) => {
  834. productImages.value.splice(e.index, 1);
  835. };
  836. const onProductImageGgDelete = (e) => {
  837. console.log(e)
  838. if (e == "oneTemplate") {
  839. oneTemplate.value.imgs = []
  840. } else {
  841. attrTable.value[e].imgs = []
  842. }
  843. productImagesGg.value.splice(e.index, 1);
  844. };
  845. // 编辑器相关方法
  846. const onEditorInput = (e) => {
  847. descriptionText.value = e.text || '';
  848. formData.value.content = e.html || '';
  849. };
  850. const onEditorInit = (editor) => {
  851. editorIns.value = editor;
  852. };
  853. const onOverMax = (e) => {
  854. uni.showToast({
  855. title: '内容长度超出限制',
  856. icon: 'none'
  857. });
  858. };
  859. const onUpinImage = (tempFiles, editorCtx) => {
  860. const filePath = tempFiles[0].tempFilePath || tempFiles[0].path;
  861. editorCtx.insertImage({
  862. src: filePath,
  863. width: '80%',
  864. success: () => {
  865. uni.showToast({
  866. title: '图片插入成功',
  867. icon: 'success'
  868. });
  869. }
  870. });
  871. };
  872. const validateForm = () => {
  873. try {
  874. if (!isProductCenter.value) {
  875. // 检查图片上传
  876. if (previewImages.value.length === 0) {
  877. uni.showToast({
  878. title: '请上传商品预览图',
  879. icon: 'none'
  880. });
  881. return;
  882. }
  883. if (productImages.value.length === 0) {
  884. uni.showToast({
  885. title: '请上传商品图片',
  886. icon: 'none'
  887. });
  888. return;
  889. }
  890. if (formData.value.sort && !/^\d+$/.test(formData.value.sort)) {
  891. uni.showToast({
  892. title: '排序号必须是整数',
  893. icon: 'none'
  894. });
  895. return;
  896. }
  897. }
  898. // if (productImagesGg.value.length === 0) {
  899. // uni.showToast({
  900. // title: '请上传商品规格图片',
  901. // icon: 'none'
  902. // });
  903. // return;
  904. // }
  905. // 验证数字字段
  906. // if (!/^\d+(\.\d+)?$/.test(formData.value.weight)) {
  907. // uni.showToast({
  908. // title: '重量格式不正确',
  909. // icon: 'none'
  910. // });
  911. // return;
  912. // }
  913. // if (!/^\d+(\.\d+)?$/.test(formData.value.laborCost)) {
  914. // uni.showToast({
  915. // title: '工费格式不正确',
  916. // icon: 'none'
  917. // });
  918. // return;
  919. // }
  920. // if (!/^\d+(\.\d+)?$/.test(formData.value.additionalFee)) {
  921. // uni.showToast({
  922. // title: '附加费格式不正确',
  923. // icon: 'none'
  924. // });
  925. // return;
  926. // }
  927. // if (!/^\d+$/.test(formData.value.stock)) {
  928. // uni.showToast({
  929. // title: '库存必须是整数',
  930. // icon: 'none'
  931. // });
  932. // return;
  933. // }
  934. // if (!/^\d+$/.test(formData.value.barCode)) {
  935. // uni.showToast({
  936. // title: '商品编号必须是整数',
  937. // icon: 'none'
  938. // });
  939. // return;
  940. // }
  941. const valid = formRef.value.validate();
  942. return valid;
  943. } catch (error) {
  944. console.error('表单验证失败:', error);
  945. return false;
  946. }
  947. };
  948. const validateAttr = (item)=>{
  949. if (!item.image) {
  950. uni.showToast({
  951. title: '请上传商品规格图片',
  952. icon: 'none'
  953. });
  954. return false;
  955. }
  956. // 验证数字字段
  957. if (!/^\d+(\.\d+)?$/.test(item.weight)) {
  958. uni.showToast({
  959. title: '重量格式不正确',
  960. icon: 'none'
  961. });
  962. return false;
  963. }
  964. if (!/^\d+(\.\d+)?$/.test(item.price)) {
  965. uni.showToast({
  966. title: '工费格式不正确',
  967. icon: 'none'
  968. });
  969. return false;
  970. }
  971. if (!/^\d+(\.\d+)?$/.test(item.additionalAmount)) {
  972. uni.showToast({
  973. title: '附加费格式不正确',
  974. icon: 'none'
  975. });
  976. return false;
  977. }
  978. if (!/^\d+$/.test(item.stock)) {
  979. uni.showToast({
  980. title: '库存必须是整数',
  981. icon: 'none'
  982. });
  983. return false;
  984. }
  985. if (!/^\d+$/.test(item.barCode)) {
  986. uni.showToast({
  987. title: '商品编号必须是整数',
  988. icon: 'none'
  989. });
  990. return false;
  991. }
  992. return true
  993. }
  994. const submitForm = async () => {
  995. const valid = validateForm();
  996. if (valid) {
  997. const submitData = {
  998. ...formData.value
  999. };
  1000. let urlString = '';
  1001. console.log(previewImages.value[0])
  1002. if (isProductCenter.value) {
  1003. submitData.image = previewImages.value[0].url;
  1004. urlString = JSON.stringify(productImages.value.map(item => item.url));
  1005. } else {
  1006. submitData.image = previewImages.value[0]?.info?.url || previewImages.value[0]?.url;
  1007. urlString = JSON.stringify(productImages.value.map(item => item?.info?.url || item?.url));
  1008. }
  1009. submitData.sliderImage = urlString;
  1010. submitData.cateId = formData.value.categoryIds.join(',');
  1011. submitData.merchantId = parseInt(merchantInfo.id);
  1012. submitData.specType = true;
  1013. submitData.isSub = false;
  1014. // 规格长度为0
  1015. console.log(attr.value,attrTable.value)
  1016. if (!attr.value || attr.value.length <= 0 || !attrTable.value || attrTable.value.length <= 0) {
  1017. return Toast({
  1018. title: "请添加商品规格!"
  1019. });
  1020. }
  1021. const pass = true;
  1022. submitData.attr = attr.value.map(item => {
  1023. return {
  1024. attrName: item.attrName,
  1025. attrValues: item.attrValue.join(','),
  1026. }
  1027. })
  1028. submitData.attrValue = attrTable.value.map(item => {
  1029. if(!validateAttr(item)){
  1030. pass = false;
  1031. }
  1032. console.log(typeof(item.attrValue))
  1033. return {
  1034. ...item,
  1035. id: 0,
  1036. productId: 0,
  1037. attrValue: typeof(item.attrValue)=='object'?JSON.stringify(item.attrValue):item.attrValue,
  1038. image: item.image || item.imgs[0]?.info?.url || item.imgs[0]?.url
  1039. }
  1040. })
  1041. console.dir(submitData)
  1042. if(!pass) return false;
  1043. // return false;
  1044. // submitData.attr = [{
  1045. // "attrName": "规格",
  1046. // "attrValues": "默认",
  1047. // "id": 0
  1048. // }];
  1049. // submitData.attrValue = [{
  1050. // additionalAmount: formData.value.additionalFee,
  1051. // attrValue: "{\"规格\":\"默认\"}",
  1052. // barCode: formData.value.barCode,
  1053. // image: productImagesGg.value[0]?.info?.url || productImagesGg.value[0].url,
  1054. // price: formData.value.laborCost,
  1055. // stock: formData.value.stock,
  1056. // weight: formData.value.weight
  1057. // }];
  1058. if (productId.value && !isProductCenter.value) {
  1059. const {
  1060. data
  1061. } = await productUpdate(submitData);
  1062. uni.showToast({
  1063. title: '修改成功',
  1064. icon: 'success'
  1065. });
  1066. } else {
  1067. const {
  1068. data
  1069. } = await productSave(submitData);
  1070. uni.showToast({
  1071. title: '发布成功',
  1072. icon: 'success'
  1073. });
  1074. }
  1075. // 编辑时,返回上个页面
  1076. if(!!productId.value && !isProductCenter.value){
  1077. uni.navigateBack()
  1078. }else{
  1079. uni.redirectTo({
  1080. url: '/pages/merchantCenters/productManagement'
  1081. })
  1082. }
  1083. }
  1084. }
  1085. // 重置表单(用于新建场景)
  1086. const resetForm = () => {
  1087. formData.value = {
  1088. categoryIds: [],
  1089. categoryDisplayName: '',
  1090. tempName: '',
  1091. tempIds: [],
  1092. storeName: '',
  1093. keyword: '',
  1094. storeInfo: '',
  1095. unitName: '',
  1096. metalType: '',
  1097. weight: '',
  1098. laborCost: '',
  1099. additionalFee: '',
  1100. sort: '',
  1101. content: '',
  1102. stock: '',
  1103. barCode: ''
  1104. };
  1105. previewImages.value = [];
  1106. productImages.value = [];
  1107. productImagesGg.value = [];
  1108. descriptionText.value = '';
  1109. // 清除校验状态
  1110. if (formRef.value) {
  1111. formRef.value.clearValidate();
  1112. }
  1113. }
  1114. const editCatePage = () => {
  1115. if (attrTable.value && attrTable.value.length) {
  1116. historyAttr = []
  1117. historyTable = {}
  1118. // 防止刷新数据时已填数据丢失
  1119. attrTable.value.forEach(item => {
  1120. historyAttr.push(item.attrName)
  1121. historyTable[item.attrName] = {
  1122. ...item
  1123. }
  1124. })
  1125. }
  1126. console.log(historyAttr, historyTable)
  1127. uni.navigateTo({
  1128. url: "/pages/merchantCenters/productCate/productCate"
  1129. })
  1130. }
  1131. const setAllAttr = () => {
  1132. if(!validateAttr(oneTemplate.value)) return false;
  1133. uni.showModal({
  1134. title: '提示',
  1135. content: '所有商品规格将修改为当前配置',
  1136. confirmText: '确认',
  1137. cancelText: "取消",
  1138. success(res) {
  1139. if (res.confirm) {
  1140. console.log(oneTemplate.value)
  1141. console.log("确认了", attrTable.value);
  1142. if (attrTable.value && attrTable.value.length > 0) {
  1143. attrTable.value = attrTable.value.map(item => {
  1144. return {
  1145. ...item,
  1146. ...oneTemplate.value
  1147. }
  1148. })
  1149. }
  1150. }
  1151. }
  1152. })
  1153. }
  1154. function attrFormat(arr) {
  1155. // console.log('arr', arr)
  1156. let data = [];
  1157. const res = [];
  1158. return format(arr);
  1159. function format(arr) {
  1160. console.log("attrFormat", arr)
  1161. if (arr.length > 1) {
  1162. arr.forEach((v, i) => {
  1163. if (i === 0) data = arr[i]["attrValue"];
  1164. const tmp = [];
  1165. if (!data) return;
  1166. data.forEach(function(vv) {
  1167. arr[i + 1] &&
  1168. arr[i + 1]["attrValue"] &&
  1169. arr[i + 1]["attrValue"].forEach(g => {
  1170. const rep2 =
  1171. (i !== 0 ? "" : arr[i]["attrName"] + "_") +
  1172. vv +
  1173. "$&" +
  1174. arr[i + 1]["attrName"] +
  1175. "_" +
  1176. g;
  1177. tmp.push(rep2);
  1178. if (i === arr.length - 2) {
  1179. const rep4 = {
  1180. additionalAmount: "",
  1181. image: "",
  1182. price: "",
  1183. cost: 0,
  1184. otPrice: 0,
  1185. stock: "",
  1186. barCode: "",
  1187. weight: "",
  1188. volume: 0,
  1189. brokerage: 0,
  1190. brokerage_two: 0,
  1191. imgs: []
  1192. };
  1193. rep2.split("$&").forEach((h, k) => {
  1194. const rep3 = h.split("_");
  1195. if (!rep4["attrValue"]) rep4["attrValue"] = {};
  1196. rep4["attrValue"][rep3[0]] =
  1197. rep3.length > 1 ? rep3[1] : "";
  1198. });
  1199. for (const attrValueKey in rep4.attrValue) {
  1200. rep4[attrValueKey] = rep4.attrValue[attrValueKey];
  1201. }
  1202. res.push(rep4);
  1203. }
  1204. });
  1205. });
  1206. data = tmp.length ? tmp : [];
  1207. });
  1208. } else {
  1209. const dataArr = [];
  1210. arr.forEach((v, k) => {
  1211. // console.log("v['attrValue']", v['attrValue'])
  1212. v["attrValue"] &&
  1213. v["attrValue"].forEach((vv, kk) => {
  1214. dataArr[kk] = v["attrName"] + "_" + vv;
  1215. res[kk] = {
  1216. additionalAmount: "",
  1217. image: "",
  1218. price: "",
  1219. cost: 0,
  1220. otPrice: 0,
  1221. stock: "",
  1222. barCode: "",
  1223. weight: "",
  1224. volume: 0,
  1225. brokerage: 0,
  1226. brokerage_two: 0,
  1227. attrValue: {
  1228. [v["attrName"]]: vv
  1229. },
  1230. imgs: []
  1231. };
  1232. // Object.values(res[kk].attrValue).forEach((v, i) => {
  1233. // res[kk]['value' + i] = v
  1234. // })
  1235. for (const attrValueKey in res[kk].attrValue) {
  1236. res[kk][attrValueKey] = res[kk].attrValue[attrValueKey];
  1237. }
  1238. });
  1239. });
  1240. data.push(dataArr.join("$&"));
  1241. }
  1242. // console.log(res)
  1243. const result = []
  1244. res.forEach(item => {
  1245. let attrName = [];
  1246. for (const name in item.attrValue) {
  1247. attrName.push(item.attrValue[name]);
  1248. }
  1249. item.attrName = attrName.join(',');
  1250. // 防止刷新数据时已填数据丢失
  1251. if (historyAttr.includes(item.attrName)) {
  1252. console.log(historyAttr, item.attrName, historyTable[item.attrName])
  1253. item = {
  1254. ...historyTable[item.attrName]
  1255. }
  1256. }
  1257. result.push(item)
  1258. console.log("res-item==>", item)
  1259. })
  1260. return result;
  1261. }
  1262. }
  1263. </script>
  1264. <style scoped lang="scss">
  1265. .container {
  1266. background-color: #f9f7f0;
  1267. min-height: 100vh;
  1268. padding-bottom: 140rpx;
  1269. }
  1270. .form-container {
  1271. padding: 16rpx;
  1272. }
  1273. .upload-section {
  1274. margin-top: 20rpx;
  1275. margin-bottom: 20rpx;
  1276. background: white;
  1277. border-radius: 16rpx;
  1278. padding: 30rpx;
  1279. }
  1280. .upload-item {
  1281. margin-bottom: 40rpx;
  1282. &:last-child {
  1283. margin-bottom: 0;
  1284. }
  1285. }
  1286. .upload-label {
  1287. display: block;
  1288. font-size: 28rpx;
  1289. color: #333;
  1290. margin-bottom: 20rpx;
  1291. position: relative;
  1292. }
  1293. .required {
  1294. position: absolute;
  1295. left: -9px;
  1296. color: #f56c6c;
  1297. line-height: 20px;
  1298. font-size: 20px;
  1299. top: 3px;
  1300. }
  1301. .upload-btn {
  1302. display: flex;
  1303. flex-direction: column;
  1304. align-items: center;
  1305. justify-content: center;
  1306. width: 5rem;
  1307. height: 5rem;
  1308. border: 1rpx dashed #DCDFE6;
  1309. border-radius: 8rpx;
  1310. background: #fff;
  1311. }
  1312. .upload-tip {
  1313. font-size: 20rpx;
  1314. color: #666;
  1315. margin-top: 6rpx;
  1316. }
  1317. .format-tip {
  1318. display: block;
  1319. font-size: 24rpx;
  1320. color: #999;
  1321. margin-top: 15rpx;
  1322. }
  1323. .card-title {
  1324. font-size: 36rpx;
  1325. color: #333;
  1326. line-height: 60rpx;
  1327. margin-bottom: 20rpx;
  1328. }
  1329. :deep(.u-form-item) {
  1330. background-color: #fff;
  1331. border-radius: 16rpx;
  1332. padding: 8rpx 30rpx;
  1333. box-sizing: border-box;
  1334. margin-bottom: 20rpx;
  1335. }
  1336. :deep(.u-form-item__body__left__content__label) {
  1337. font-size: 28rpx !important;
  1338. color: #333 !important;
  1339. }
  1340. :deep(.u-radio-group--row) {
  1341. justify-content: flex-end;
  1342. }
  1343. .btn-view {
  1344. position: fixed;
  1345. bottom: 0;
  1346. left: 0;
  1347. right: 0;
  1348. background-color: #FFF;
  1349. padding: 20rpx 30rpx;
  1350. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
  1351. z-index: 5;
  1352. }
  1353. .submit {
  1354. height: 88rpx;
  1355. line-height: 88rpx;
  1356. background: #F8C008;
  1357. border-radius: 16rpx;
  1358. font-size: 32rpx;
  1359. font-weight: bold;
  1360. color: #333333;
  1361. border: none;
  1362. width: 100%;
  1363. &:active {
  1364. opacity: 0.8;
  1365. }
  1366. }
  1367. .unit {
  1368. color: #999;
  1369. font-size: 28rpx;
  1370. margin-left: 10rpx;
  1371. }
  1372. /* 编辑器样式 */
  1373. .editor-section {
  1374. background: white;
  1375. border-radius: 16rpx;
  1376. margin-bottom: 20rpx;
  1377. overflow: hidden;
  1378. }
  1379. .editor-header {
  1380. display: flex;
  1381. justify-content: space-between;
  1382. align-items: center;
  1383. padding: 30rpx;
  1384. border-bottom: 1rpx solid #f0f0f0;
  1385. }
  1386. .editor-label {
  1387. font-size: 28rpx;
  1388. color: #333;
  1389. font-weight: 500;
  1390. }
  1391. .word-count {
  1392. font-size: 24rpx;
  1393. color: #999;
  1394. }
  1395. :deep(.sp-editor) {
  1396. min-height: 400rpx;
  1397. }
  1398. /* 弹窗样式 */
  1399. .popup-content {
  1400. background: #fff;
  1401. border-radius: 20rpx 20rpx 0 0;
  1402. padding-bottom: env(safe-area-inset-bottom);
  1403. }
  1404. .popup-header {
  1405. padding: 30rpx;
  1406. text-align: center;
  1407. border-bottom: 1rpx solid #f0f0f0;
  1408. position: relative;
  1409. }
  1410. .popup-title {
  1411. font-size: 32rpx;
  1412. font-weight: 600;
  1413. color: #333;
  1414. }
  1415. .popup-actions {
  1416. display: flex;
  1417. padding: 30rpx;
  1418. gap: 20rpx;
  1419. }
  1420. .action-btn {
  1421. flex: 1;
  1422. height: 80rpx;
  1423. line-height: 80rpx;
  1424. border-radius: 12rpx;
  1425. font-size: 28rpx;
  1426. border: none;
  1427. &.cancel {
  1428. background: #f0f0f0;
  1429. color: #666;
  1430. }
  1431. &.confirm {
  1432. background: #F8C008;
  1433. color: #333;
  1434. }
  1435. }
  1436. :deep(.u-tabs__wrapper__nav__line) {
  1437. background-color: #F8C008 !important;
  1438. }
  1439. :deep(.u-form-item__body__right__message) {
  1440. text-align: right;
  1441. }
  1442. :deep(.u-textarea__field) {
  1443. text-align: right;
  1444. }
  1445. .pro-cate-container {
  1446. width: 100%;
  1447. padding: 16rpx;
  1448. background-color: #fff;
  1449. border-radius: 16rpx;
  1450. margin-bottom: 16rpx;
  1451. .cate-title {
  1452. font-size: 28rpx;
  1453. line-height: 44rpx;
  1454. color: #333;
  1455. margin-bottom: 16rpx;
  1456. }
  1457. .cate-add {
  1458. width: 100%;
  1459. height: 88rpx;
  1460. line-height: 88rpx;
  1461. text-align: center;
  1462. border-radius: 16rpx;
  1463. background-color: #FEF8E6;
  1464. color: #F8C008;
  1465. font-size: 32rpx;
  1466. font-weight: bold;
  1467. margin-top: 16rpx;
  1468. }
  1469. .cate-list {
  1470. .cate-item {
  1471. width: 100%;
  1472. background-color: #F9F7F0;
  1473. border-radius: 16rpx;
  1474. padding: 16rpx;
  1475. margin-bottom: 16rpx;
  1476. .upload-btn {
  1477. display: flex;
  1478. flex-direction: column;
  1479. align-items: center;
  1480. justify-content: center;
  1481. width: 100rpx;
  1482. height: 100rpx;
  1483. border: 1rpx dashed #DCDFE6;
  1484. border-radius: 8rpx;
  1485. background: #fff;
  1486. }
  1487. .cate-pic-line {
  1488. width: 100%;
  1489. height: 100rpx;
  1490. display: flex;
  1491. justify-content: space-between;
  1492. align-items: center;
  1493. margin-bottom: 16rpx;
  1494. ::v-deep .u-upload {
  1495. flex: 0;
  1496. margin-right: 16rpx;
  1497. width: 100rpx;
  1498. height: 100rpx;
  1499. }
  1500. ::v-deep .u-upload__wrap__preview {
  1501. margin: 0;
  1502. width: 100rpx;
  1503. height: 100rpx;
  1504. }
  1505. ::v-deep .u-upload__wrap__preview__image {
  1506. width: 100rpx !important;
  1507. height: 100rpx !important;
  1508. }
  1509. .cate-pic {
  1510. width: 100rpx;
  1511. height: 100rpx;
  1512. border-radius: 8rpx;
  1513. background-color: #fff;
  1514. margin-right: 16rpx;
  1515. }
  1516. .cate-name {
  1517. width: 100rpx;
  1518. flex: 1;
  1519. color: #333;
  1520. font-size: 28rpx;
  1521. line-height: 44rpx;
  1522. }
  1523. }
  1524. .cate-ipt-line {
  1525. width: 100%;
  1526. display: flex;
  1527. align-items: center;
  1528. justify-content: space-between;
  1529. flex-wrap: nowrap;
  1530. padding-bottom: 16rpx;
  1531. .cate-ipt {
  1532. width: 48%;
  1533. flex: 1;
  1534. height: 100rpx;
  1535. background-color: #fff;
  1536. border-radius: 16rpx;
  1537. padding: 0 16rpx !important;
  1538. font-size: 28rpx;
  1539. .unit {
  1540. color: #333;
  1541. }
  1542. }
  1543. }
  1544. }
  1545. }
  1546. }
  1547. </style>