applyNew.uvue 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504
  1. <template>
  2. <uni-navbar-lite :showRight=false title="物料申请"></uni-navbar-lite>
  3. <view class="page-container">
  4. <scroll-view class="page-content">
  5. <view class="section">
  6. <view class="search-bar">
  7. <view class="search-box">
  8. <image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
  9. <input class="search-input" type="text" placeholder="请输入关键字查询" v-model="keyword" @input="handleSearch" />
  10. <view v-if="keyword.length > 0" class="clear-btn" @tap="clearKeyword">
  11. <text class="clear-btn-text">×</text>
  12. </view>
  13. <view class="search-btn" @tap="handleSearch">
  14. <text class="search-btn-text">搜索</text>
  15. </view>
  16. </view>
  17. </view>
  18. <view class="status-tabs">
  19. <scroll-view class="scroll-view_H" direction="horizontal" :show-scrollbar="false">
  20. <view
  21. v-for="(cat, idx) in categories"
  22. :key="idx"
  23. class="status-tab"
  24. :class="{ 'active': currentStatus === cat.id }"
  25. @click="switchStatus(cat.id)"
  26. >
  27. <text class="status-tab-text" :class="{ 'active-text': currentStatus === cat.id }">{{ cat.name }}</text>
  28. </view>
  29. </scroll-view>
  30. </view>
  31. </view>
  32. <view class="section">
  33. <view class="section-header">
  34. <view class="section-header-left">
  35. <view class="section-indicator"></view>
  36. <text class="section-title">物料列表</text>
  37. </view>
  38. <view class="add-material-btn" @click="showAddMaterialModal = true">
  39. <text class="add-material-text">新物料</text>
  40. </view>
  41. </view>
  42. <view class="material-list">
  43. <view
  44. v-for="(item, index) in dataList"
  45. :key="item.itemId + '_' + page"
  46. class="list-item"
  47. @click="handleItemClick(item, index)"
  48. >
  49. <view class="item-header">
  50. <view class="item-info">
  51. <view class="item-name-row">
  52. <text class="item-name" :class="{'status-0': getAuditStatus(item) == 0, 'status-2': getAuditStatus(item) == 3}">{{ getItemName(item) }}</text>
  53. <text class="item-measure">{{ getMeasureName(item) }}</text>
  54. </view>
  55. <view class="item-sub-row">
  56. <text class="item-sub-title">{{ getItemTypeName(item) }}</text>
  57. <text class="item-stock">库存: {{ getStockNum(item) }}</text>
  58. </view>
  59. </view>
  60. <view class="item-actions">
  61. <view v-if="getAuditStatus(item) == 0 && !item.isSelected!" class="delete-btn" @click.stop="handleDeleteMaterial(item)">
  62. <text class="delete-icon">×</text>
  63. </view>
  64. <view v-if="item.isSelected" class="added-btn">
  65. <text class="added-icon">已选</text>
  66. </view>
  67. <view v-else-if="isItemDisabled(item)" class="add-btn disabled">
  68. <text class="add-icon">+</text>
  69. </view>
  70. <view v-else class="add-btn" @click.stop="addToSelected(item)">
  71. <text class="add-icon">+</text>
  72. </view>
  73. </view>
  74. </view>
  75. </view>
  76. </view>
  77. <!-- 手动分页 -->
  78. <view class="pagination">
  79. <view class="page-btn" :class="{disabled: page <= 1}" @click="prevPage">
  80. <text class="page-btn-text"><</text>
  81. </view>
  82. <text class="page-info">第{{ page }}页 / 共{{ totalPages }}页</text>
  83. <view class="page-btn" :class="{disabled: page >= totalPages}" @click="nextPage">
  84. <text class="page-btn-text">></text>
  85. </view>
  86. </view>
  87. </view>
  88. <view class="section">
  89. <view class="section-header">
  90. <view class="section-header-left">
  91. <view class="section-indicator"></view>
  92. <text class="section-title">已选列表</text>
  93. </view>
  94. </view>
  95. <view class="selected-list">
  96. <view v-if="selectedItems.length === 0" class="empty-tip">
  97. <text class="empty-tip-text">请选择物料</text>
  98. </view>
  99. <view
  100. v-else
  101. v-for="(selItem, selIndex) in selectedItems"
  102. :key="selIndex"
  103. class="selected-item"
  104. >
  105. <text class="selected-item-name">{{ selItem.itemName }}</text>
  106. <text class="selected-item-measure">{{ selItem.measureName }}</text>
  107. <view class="quantity-control">
  108. <view class="qty-btn" @click="decreaseQty(selIndex)">
  109. <text class="qty-btn-text">-</text>
  110. </view>
  111. <input
  112. class="quantity-input"
  113. type="digit"
  114. v-model="selItem.qty"
  115. placeholder="数量"
  116. />
  117. <view class="qty-btn" @click="increaseQty(selIndex)">
  118. <text class="qty-btn-text">+</text>
  119. </view>
  120. </view>
  121. <view class="delete-btn" @click="removeSelected(selIndex)">
  122. <text class="delete-icon">×</text>
  123. </view>
  124. </view>
  125. </view>
  126. </view>
  127. </scroll-view>
  128. <!-- 底部按钮 -->
  129. <view class="bottom-buttons">
  130. <view class="btn-row">
  131. <button class="save-btn" @click="saveApplication">保存</button>
  132. <button class="confirm-btn" @click="confirmApplication">确认申请</button>
  133. </view>
  134. </view>
  135. <!-- 新增物料弹窗 -->
  136. <view v-if="showAddMaterialModal" class="picker-modal">
  137. <view class="modal-mask" @click="showAddMaterialModal = false"></view>
  138. <view class="modal-content">
  139. <view class="modal-header">
  140. <text class="modal-title">新增物料</text>
  141. <text class="modal-close" @click="showAddMaterialModal = false">取消</text>
  142. </view>
  143. <view class="form-item-input">
  144. <text class="label-picker">物料名称</text>
  145. <view class="view-input-picker">
  146. <input class="input-picker" type="text" placeholder="请输入物料名称" v-model="newMaterial.itemName" />
  147. </view>
  148. </view>
  149. <view class="form-item-input">
  150. <text class="label-picker">规格型号</text>
  151. <view class="view-input-picker">
  152. <input class="input-picker" type="text" placeholder="请输入规格型号" v-model="newMaterial.specification" />
  153. </view>
  154. </view>
  155. <view class="form-item-input">
  156. <text class="label-picker">单位</text>
  157. <view class="view-input-picker measure-input-row">
  158. <input class="input-picker" type="text" placeholder="请输入或选择单位" v-model="newMaterial.measureName" />
  159. <view class="pick-btn" @click="showMeasurePicker = true">
  160. <text class="pick-btn-text">选择</text>
  161. </view>
  162. </view>
  163. </view>
  164. <view class="form-item-input">
  165. <text class="label-picker">产品链接</text>
  166. <view class="view-input-picker">
  167. <input class="input-picker" type="url" placeholder="请输入产品链接" v-model="newMaterial.productUrl" />
  168. </view>
  169. </view>
  170. <view class="form-item-input">
  171. <text class="label-picker">图片(最多3张)</text>
  172. <view class="upload-wrapper">
  173. <UploadImage
  174. :maxCount="3"
  175. :modelValue="newMaterial.imageUrls"
  176. businessType="material"
  177. @update:modelValue="handleImageUpdate"
  178. @delete="handleImageDelete"
  179. />
  180. </view>
  181. </view>
  182. <view class="form-item-btn">
  183. <button class="btn-primary" @click="handleAddMaterial">
  184. 保存
  185. </button>
  186. </view>
  187. </view>
  188. </view>
  189. <!-- 单位选择弹窗 -->
  190. <view v-if="showMeasurePicker" class="picker-modal">
  191. <view class="modal-mask" @click="showMeasurePicker = false"></view>
  192. <view class="modal-content">
  193. <view class="modal-header">
  194. <text class="modal-title">选择单位</text>
  195. <text class="modal-close" @click="showMeasurePicker = false">取消</text>
  196. </view>
  197. <scroll-view class="measure-list" :scroll-y="true">
  198. <view
  199. v-for="(item, index) in measureList"
  200. :key="index"
  201. class="measure-item"
  202. @click="selectMeasure(item)"
  203. >
  204. <text class="measure-name">{{ item['measureName'] }}</text>
  205. </view>
  206. <view v-if="measureList.length === 0" class="empty-tip">
  207. <text class="empty-tip-text">暂无单位数据</text>
  208. </view>
  209. </scroll-view>
  210. </view>
  211. </view>
  212. <!-- 删除确认弹窗 -->
  213. <view v-if="showDeleteConfirm" class="delete-confirm-modal">
  214. <view class="delete-confirm-mask" @click="showDeleteConfirm = false"></view>
  215. <view class="delete-confirm-content">
  216. <text class="delete-confirm-title">确认删除</text>
  217. <text class="delete-confirm-text">确定要删除这张图片吗?</text>
  218. <view class="delete-confirm-btns">
  219. <view class="delete-confirm-cancel" @click="showDeleteConfirm = false">
  220. <text class="delete-confirm-cancel-text">取消</text>
  221. </view>
  222. <view class="delete-confirm-ok" @click="confirmDeleteImage">
  223. <text class="delete-confirm-ok-text">确定</text>
  224. </view>
  225. </view>
  226. </view>
  227. </view>
  228. </view>
  229. </template>
  230. <script setup lang="uts">
  231. import { ref, computed, onBeforeUnmount, onMounted } from 'vue'
  232. import { getItemTypeListByParentId, getItemList, savePurchaseApply, confirmPurchaseApply, addMaterial, getMeasureList, deleteMaterial } from '../../api/apply/index'
  233. import UploadImage from '../../components/upload-image/upload-image.uvue'
  234. import type { UploadResponse } from '../../types/workbench'
  235. let searchTimer: number | null = null
  236. type CategoryItem = { id: string; name: string; code: string }
  237. type Item = {
  238. itemId: string;
  239. itemName: string;
  240. itemCode: string;
  241. itemTypeName: string;
  242. measureName: string;
  243. specification: string;
  244. imageUrls: string;
  245. productUrl: string;
  246. auditStatus: number;
  247. stockNum: number;
  248. isSelected?: boolean;
  249. }
  250. type SelectedItem = { itemId: string; itemName: string; itemTypeName: string; measureName: string; qty: string }
  251. const keyword = ref<string>("")
  252. const dataList = ref<Item[]>([])
  253. const selectedItems = ref<SelectedItem[]>([])
  254. const page = ref<number>(1)
  255. const pageSize: number = 10
  256. const total = ref<number>(0)
  257. const totalPages = computed((): number => {
  258. if (total.value <= 0) return 1
  259. return Math.ceil(total.value / pageSize)
  260. })
  261. const hasMore = ref<boolean>(true)
  262. const loading = ref<boolean>(false)
  263. const refreshing = ref<boolean>(false)
  264. const categories = ref<CategoryItem[]>([])
  265. const dealLoad = ref<boolean>(false)
  266. let currentStatus = ref<string>('')
  267. const isSearching = ref<boolean>(false)
  268. // 新增物料相关
  269. const showAddMaterialModal = ref<boolean>(false)
  270. const showMeasurePicker = ref<boolean>(false)
  271. const measureList = ref<UTSJSONObject[]>([])
  272. const showDeleteConfirm = ref<boolean>(false)
  273. const deleteImageIndex = ref<number>(0)
  274. type NewMaterial = {
  275. itemName: string
  276. specification: string
  277. measureName: string
  278. imageUrls: UploadResponse[]
  279. productUrl: string
  280. }
  281. const newMaterial = ref<NewMaterial>({
  282. itemName: '',
  283. specification: '',
  284. measureName: '',
  285. imageUrls: [],
  286. productUrl: ''
  287. })
  288. // 加载单位列表
  289. const loadMeasureList = (): void => {
  290. getMeasureList().then((res: any) => {
  291. const data = res as UTSJSONObject
  292. const list = data['data']
  293. if (list != null) {
  294. measureList.value = list as UTSJSONObject[]
  295. }
  296. }).catch(() => {
  297. measureList.value = []
  298. })
  299. }
  300. // 选择单位
  301. const selectMeasure = (item: UTSJSONObject): void => {
  302. const measureName = item['measureName'] != null ? item['measureName'].toString() : ''
  303. newMaterial.value.measureName = measureName
  304. showMeasurePicker.value = false
  305. }
  306. // 获取当前选中的分类code
  307. const getCurrentCategoryCode = (): string => {
  308. const currentCat = categories.value.find((c: CategoryItem) => c.id === currentStatus.value)
  309. return currentCat != null ? currentCat.code : ''
  310. }
  311. // 加载物料列表
  312. const loadItems = (): void => {
  313. loading.value = true
  314. const itemTypeCode = getCurrentCategoryCode()
  315. getItemList(itemTypeCode, page.value, pageSize, keyword.value).then((res: any) => {
  316. const data = res as UTSJSONObject
  317. const rows = data['rows']
  318. const totalNum = data['total']
  319. if (totalNum != null) {
  320. total.value = totalNum as number
  321. }
  322. if (rows != null) {
  323. const listData = rows as UTSJSONObject[]
  324. if (listData.length > 0) {
  325. const arr: Item[] = []
  326. const selectedIds = selectedItems.value.map((s: SelectedItem) => s.itemId)
  327. listData.forEach((item: UTSJSONObject) => {
  328. const itemId = item['itemId'] != null ? item['itemId'].toString() : ''
  329. let auditStatus = -1
  330. const auditStatusVal = item['auditStatus']
  331. if (auditStatusVal != null) {
  332. if (typeof auditStatusVal === 'number') {
  333. auditStatus = auditStatusVal as number
  334. } else {
  335. auditStatus = parseInt(auditStatusVal.toString())
  336. }
  337. }
  338. let stockNum = 0
  339. const stockNumVal = item['stockNum']
  340. if (stockNumVal != null) {
  341. if (typeof stockNumVal === 'number') {
  342. stockNum = stockNumVal as number
  343. } else {
  344. stockNum = parseFloat(stockNumVal.toString())
  345. }
  346. }
  347. const it: Item = {
  348. itemId: itemId,
  349. itemName: item['itemName'] != null ? item['itemName'].toString() : '',
  350. itemCode: item['itemCode'] != null ? item['itemCode'].toString() : '',
  351. itemTypeName: item['itemTypeName'] != null ? item['itemTypeName'].toString() : '',
  352. measureName: item['measureName'] != null ? item['measureName'].toString() : '',
  353. specification: item['specification'] != null ? item['specification'].toString() : '',
  354. imageUrls: item['imageUrls'] != null ? item['imageUrls'].toString() : '',
  355. productUrl: item['productUrl'] != null ? item['productUrl'].toString() : '',
  356. isSelected: selectedIds.includes(itemId),
  357. auditStatus: auditStatus,
  358. stockNum: stockNum
  359. }
  360. arr.push(it)
  361. })
  362. dataList.value = arr
  363. loading.value = false
  364. return
  365. }
  366. }
  367. dataList.value = []
  368. loading.value = false
  369. }).catch(() => {
  370. dataList.value = []
  371. loading.value = false
  372. })
  373. }
  374. // 上一页
  375. const prevPage = (): void => {
  376. if (page.value > 1) {
  377. page.value--
  378. loadItems()
  379. }
  380. }
  381. // 下一页
  382. const nextPage = (): void => {
  383. if (page.value < totalPages.value) {
  384. page.value++
  385. loadItems()
  386. }
  387. }
  388. const loadCategories = (): void => {
  389. getItemTypeListByParentId(200).then((res: any) => {
  390. const data = res as UTSJSONObject
  391. const list = data['data']
  392. if (list != null) {
  393. const listData = list as UTSJSONObject[]
  394. if (listData.length > 0) {
  395. const arr: CategoryItem[] = []
  396. listData.forEach((item: UTSJSONObject) => {
  397. const idVal = item['itemTypeId']
  398. const nameVal = item['itemTypeName']
  399. const codeVal = item['itemTypeCode']
  400. const cat: CategoryItem = {
  401. id: idVal != null ? idVal.toString() : '',
  402. name: nameVal != null ? nameVal.toString() : '',
  403. code: codeVal != null ? codeVal.toString() : ''
  404. }
  405. arr.push(cat)
  406. })
  407. const allItem: CategoryItem = {id: '', name: '全部', code: ''}
  408. arr.unshift(allItem)
  409. categories.value = arr
  410. // 加载第一个分类的物料
  411. if (arr.length > 0) {
  412. currentStatus.value = arr[0].id
  413. loadItems()
  414. }
  415. return
  416. }
  417. }
  418. const defaultArr: CategoryItem[] = []
  419. const defaultItem: CategoryItem = {id: '', name: '全部', code: ''}
  420. defaultArr.push(defaultItem)
  421. categories.value = defaultArr
  422. }).catch(() => {
  423. const defaultArr: CategoryItem[] = []
  424. const defaultItem: CategoryItem = {id: '', name: '全部', code: ''}
  425. defaultArr.push(defaultItem)
  426. categories.value = defaultArr
  427. })
  428. }
  429. // 添加到已选列表
  430. const addToSelected = (item: any | null): void => {
  431. if (item == null) return
  432. const mdItem = item as Item
  433. // 检查审核状态
  434. if (mdItem.auditStatus != null && mdItem.auditStatus === 2) {
  435. uni.showToast({ title: '该物料已禁用,无法添加', icon: 'none'})
  436. return
  437. }
  438. // 检查是否已存在
  439. const exists = selectedItems.value.find((s: SelectedItem) => s.itemId === mdItem.itemId)
  440. if (exists != null) {
  441. uni.showToast({ title: '该物料已在选择列表中', icon: 'none'})
  442. return
  443. }
  444. const selItem: SelectedItem = {
  445. itemId: mdItem.itemId,
  446. itemName: mdItem.itemName,
  447. itemTypeName: mdItem.itemTypeName,
  448. measureName: mdItem.measureName,
  449. qty: '1'
  450. }
  451. selectedItems.value.push(selItem)
  452. // 更新 dataList 中的选中状态
  453. const idx = dataList.value.findIndex((d: Item) => d.itemId === mdItem.itemId)
  454. if (idx >= 0) {
  455. dataList.value[idx].isSelected = true
  456. }
  457. }
  458. // 删除已选
  459. const removeSelected = (index: number): void => {
  460. const removedItem = selectedItems.value[index]
  461. selectedItems.value.splice(index, 1)
  462. // 重新加载当前页数据以更新选中状态
  463. loadItems()
  464. }
  465. // 增加数量
  466. const increaseQty = (index: number): void => {
  467. const currentQty = parseInt(selectedItems.value[index].qty)
  468. selectedItems.value[index].qty = (currentQty + 1).toString()
  469. }
  470. // 减少数量
  471. const decreaseQty = (index: number): void => {
  472. const currentQty = parseInt(selectedItems.value[index].qty)
  473. if (currentQty > 1) {
  474. selectedItems.value[index].qty = (currentQty - 1).toString()
  475. }
  476. }
  477. const handleItemClick = (item: any | null, index: number): void => {
  478. if (item == null) return
  479. const mdItem = item as Item
  480. uni.navigateTo({
  481. url: `/pages/item/detail?id=${mdItem.itemId}`
  482. })
  483. }
  484. const getItemName = (item: any | null): string => {
  485. if (item == null) return ''
  486. const mdItem = item as Item
  487. return mdItem.itemName
  488. }
  489. const getItemTypeName = (item: any | null): string => {
  490. if (item == null) return ''
  491. const mdItem = item as Item
  492. return mdItem.itemTypeName
  493. }
  494. const getMeasureName = (item: any | null): string => {
  495. if (item == null) return ''
  496. const mdItem = item as Item
  497. return mdItem.measureName
  498. }
  499. const getStockNum = (item: any | null): string => {
  500. if (item == null) return '0'
  501. const mdItem = item as Item
  502. const stock = mdItem.stockNum != null ? mdItem.stockNum : 0
  503. return stock.toString()
  504. }
  505. const getAuditStatus = (item: any | null): number => {
  506. if (item == null) return -1
  507. const mdItem = item as Item
  508. return mdItem.auditStatus != null ? mdItem.auditStatus : -1
  509. }
  510. const isItemDisabled = (item: any | null): boolean => {
  511. if (item == null) return false
  512. const mdItem = item as Item
  513. const status = mdItem.auditStatus != null ? mdItem.auditStatus : -1
  514. return status == 3
  515. }
  516. // 检查物料是否已被选择
  517. const isItemSelected = (item: any | null): boolean => {
  518. if (item == null) return false
  519. const mdItem = item as Item
  520. return selectedItems.value.find((s: SelectedItem) => s.itemId === mdItem.itemId) != null
  521. }
  522. const handleSearch = (): void => {
  523. const timer = searchTimer
  524. if (timer != null) {
  525. clearTimeout(timer)
  526. }
  527. searchTimer = setTimeout(() => {
  528. page.value = 1
  529. loadItems()
  530. }, 300)
  531. }
  532. const clearKeyword = (): void => {
  533. keyword.value = ''
  534. page.value = 1
  535. loadItems()
  536. }
  537. const switchStatus = (status: string): void => {
  538. if (loading.value) {
  539. return;
  540. }
  541. if (isSearching.value) {
  542. return
  543. }
  544. isSearching.value = true
  545. currentStatus.value = status
  546. page.value = 1
  547. loadItems()
  548. setTimeout(() => {
  549. isSearching.value = false
  550. }, 100)
  551. }
  552. // 构建申请单数据
  553. const buildApplyData = (status: string): UTSJSONObject => {
  554. const applyLines: UTSJSONObject[] = []
  555. selectedItems.value.forEach((item: SelectedItem) => {
  556. const lineObj = new UTSJSONObject()
  557. lineObj['itemId'] = parseInt(item.itemId)
  558. lineObj['itemCode'] = ''
  559. lineObj['itemName'] = item.itemName
  560. lineObj['specification'] = ''
  561. lineObj['unitOfMeasure'] = ''
  562. lineObj['quantityApply'] = parseInt(item.qty)
  563. applyLines.push(lineObj)
  564. })
  565. const result = new UTSJSONObject()
  566. result['applyCode'] = ''
  567. result['applyName'] = '物料申请'
  568. result['status'] = status
  569. result['wmPurchaseApplyLineList'] = applyLines
  570. return result
  571. }
  572. // 来源页面
  573. const fromIndexPage = ref<boolean>(false)
  574. // 保存申请
  575. const saveApplication = (): void => {
  576. if (selectedItems.value.length === 0) {
  577. uni.showToast({ title: '请选择物料', icon: 'none'})
  578. return
  579. }
  580. // 构建申请单数据
  581. const applyData = buildApplyData('PREPARE')
  582. savePurchaseApply(applyData).then((res: any) => {
  583. uni.showToast({ title: '保存成功', icon: 'success'})
  584. // 清空已选列表
  585. selectedItems.value = []
  586. loadItems()
  587. // 如果从index.uvue跳转过来,返回index.uvue
  588. if (fromIndexPage.value) {
  589. setTimeout(() => {
  590. uni.navigateBack()
  591. }, 1000)
  592. }else{
  593. uni.navigateTo({
  594. url: `/pages/apply/index?from=new`
  595. })
  596. }
  597. }).catch((e) => {
  598. console.log(e)
  599. const error = e as UTSError
  600. const errMsg = error?.message
  601. uni.showToast({ title: errMsg.toString(), icon: 'none'})
  602. })
  603. }
  604. // 确认申请
  605. const confirmApplication = (): void => {
  606. if (selectedItems.value.length === 0) {
  607. uni.showToast({ title: '请选择物料', icon: 'none'})
  608. return
  609. }
  610. // 验证所有数量都大于0
  611. for (let i = 0; i < selectedItems.value.length; i++) {
  612. const qty = parseFloat(selectedItems.value[i].qty)
  613. if (qty <= 0) {
  614. uni.showToast({ title: '所有物料数量必须大于0', icon: 'none'})
  615. return
  616. }
  617. }
  618. // 构建申请单数据
  619. const applyData = buildApplyData('CONFIRMED')
  620. savePurchaseApply(applyData).then((res: any) => {
  621. uni.showToast({ title: '确认申请成功', icon: 'success'})
  622. // 清空已选列表
  623. selectedItems.value = []
  624. loadItems()
  625. // 如果从index.uvue跳转过来,返回index.uvue
  626. if (fromIndexPage.value) {
  627. setTimeout(() => {
  628. uni.navigateBack()
  629. }, 1000)
  630. }else{
  631. uni.navigateTo({
  632. url: `/pages/apply/index?from=new`
  633. })
  634. }
  635. }).catch((e) => {
  636. const error = e as UTSError
  637. const errMsg = error?.message
  638. uni.showToast({ title: errMsg.toString(), icon: 'none'})
  639. })
  640. }
  641. // 处理图片更新
  642. const handleImageUpdate = (value: UploadResponse[]): void => {
  643. newMaterial.value.imageUrls = value
  644. }
  645. // 处理图片删除
  646. const handleImageDelete = (index: number, currentList: UploadResponse[]): void => {
  647. deleteImageIndex.value = index
  648. showDeleteConfirm.value = true
  649. }
  650. const confirmDeleteImage = (): void => {
  651. newMaterial.value.imageUrls.splice(deleteImageIndex.value, 1)
  652. showDeleteConfirm.value = false
  653. }
  654. // 处理新增物料
  655. const handleAddMaterial = async (): Promise<void> => {
  656. // 验证输入
  657. if (newMaterial.value.itemName.length == 0) {
  658. uni.showToast({ title: '请输入物料名称', icon: 'none'})
  659. return
  660. }
  661. if (newMaterial.value.measureName.length == 0) {
  662. uni.showToast({ title: '请输入单位', icon: 'none'})
  663. return
  664. }
  665. try {
  666. // 构建物料数据
  667. const materialData = new UTSJSONObject()
  668. materialData['itemShortName'] = newMaterial.value.itemName
  669. materialData['itemName'] = newMaterial.value.itemName + "("+newMaterial.value.specification+")"
  670. materialData['specification'] = newMaterial.value.specification
  671. materialData['measureName'] = newMaterial.value.measureName
  672. materialData['auditStatus'] = 0 // 审核状态为0
  673. // 从 UploadResponse 数组中提取 fileName 并拼接成逗号分隔的字符串
  674. const imageFileNames = newMaterial.value.imageUrls.map((item: UploadResponse) => item.fileName).join(',')
  675. materialData['imageUrls'] = imageFileNames
  676. materialData['productUrl'] = newMaterial.value.productUrl
  677. // 调用接口保存物料
  678. await addMaterial(materialData)
  679. // 保存成功
  680. uni.showToast({ title: '物料新增成功', icon: 'success'})
  681. // 关闭弹窗
  682. showAddMaterialModal.value = false
  683. // 清空表单
  684. newMaterial.value = {
  685. itemName: '',
  686. specification: '',
  687. measureName: '',
  688. imageUrls: [],
  689. productUrl: ''
  690. } as NewMaterial
  691. // 重新加载物料列表
  692. loadItems()
  693. } catch (e) {
  694. const error = e as UTSError
  695. const errMsg = error?.message
  696. uni.showToast({ title: errMsg.toString(), icon: 'none'})
  697. }
  698. }
  699. // 处理删除物料
  700. const handleDeleteMaterial = (item: Item): void => {
  701. uni.showModal({
  702. title: '确认删除',
  703. content: `确定要删除物料「${item.itemName}」吗?`,
  704. success: (res) => {
  705. if (res.confirm) {
  706. deleteMaterial(item.itemId).then(() => {
  707. uni.showToast({
  708. title: '删除成功',
  709. icon: 'success',
  710. })
  711. loadItems()
  712. }).catch((e) => {
  713. const error = e as UTSError
  714. const errMsg = error?.message
  715. uni.showModal({
  716. title: '删除失败',
  717. content: errMsg.toString(),
  718. showCancel: false,
  719. confirmText: '确定'
  720. })
  721. })
  722. }
  723. }
  724. })
  725. }
  726. onMounted(() => {
  727. loadCategories()
  728. loadMeasureList()
  729. // 获取页面参数,判断是否从index.uvue跳转过来
  730. const pages = getCurrentPages()
  731. const currentPage = pages[pages.length - 1]
  732. const options = currentPage.options
  733. if (options != null && options.from == 'index') {
  734. fromIndexPage.value = true
  735. }
  736. })
  737. </script>
  738. <style lang="scss">
  739. .page-container {
  740. flex: 1;
  741. background-color: #f5f8fe;
  742. padding-top: env(safe-area-inset-top);
  743. background-color: #e8f0f9;
  744. padding: 0rpx 20rpx 0rpx 20rpx;
  745. }
  746. .page-content {
  747. flex: 1;
  748. padding: 0rpx;
  749. padding-bottom: 150rpx;
  750. box-sizing: border-box;
  751. }
  752. .section {
  753. margin-top: 10rpx;
  754. padding: 0 10rpx 10rpx;
  755. background: #ffffff;
  756. border-radius: 16rpx;
  757. .section-header {
  758. flex-direction: row;
  759. align-items: center;
  760. justify-content: space-between;
  761. padding: 30rpx 10rpx 20rpx;
  762. }
  763. .section-header-left {
  764. flex-direction: row;
  765. align-items: center;
  766. }
  767. .add-material-btn {
  768. padding: 8rpx 16rpx;
  769. background-color: #007aff;
  770. border-radius: 8rpx;
  771. }
  772. .add-material-text {
  773. color: #ffffff;
  774. font-size: 24rpx;
  775. font-weight: bold;
  776. }
  777. &-indicator {
  778. width: 6rpx;
  779. height: 32rpx;
  780. background-color: #007aff;
  781. border-radius: 3rpx;
  782. margin-right: 12rpx;
  783. }
  784. &-title {
  785. font-size: 36rpx;
  786. color: #333333;
  787. font-weight: bold;
  788. }
  789. }
  790. .search-bar {
  791. padding: 10rpx 3rpx;
  792. }
  793. .search-box {
  794. flex-direction: row;
  795. align-items: center;
  796. height: 72rpx;
  797. padding: 0 24rpx;
  798. background-color: #f5f5f5;
  799. border-radius: 16rpx;
  800. .search-icon {
  801. width: 32rpx;
  802. height: 32rpx;
  803. margin-right: 12rpx;
  804. }
  805. .search-input {
  806. flex: 1;
  807. font-size: 28rpx;
  808. color: #333333;
  809. }
  810. .search-btn {
  811. padding: 10rpx 20rpx;
  812. background-color: #007aff;
  813. border-radius: 8rpx;
  814. margin-left: 10rpx;
  815. }
  816. .search-btn-text {
  817. color: #ffffff;
  818. font-size: 24rpx;
  819. }
  820. .clear-btn {
  821. width: 36rpx;
  822. height: 36rpx;
  823. border-radius: 18rpx;
  824. background-color: #cccccc;
  825. align-items: center;
  826. justify-content: center;
  827. margin-left: 10rpx;
  828. }
  829. .clear-btn-text {
  830. color: #ffffff;
  831. font-size: 24rpx;
  832. font-weight: bold;
  833. }
  834. }
  835. .status-tabs {
  836. padding: 20rpx 30rpx;
  837. background-color: #ffffff;
  838. border-bottom: 1rpx solid #eeeeee;
  839. }
  840. .scroll-view_H {
  841. width: 100%;
  842. flex-direction: row;
  843. }
  844. .status-tab {
  845. padding: 16rpx 24rpx;
  846. text-align: center;
  847. margin-right: 16rpx;
  848. border-radius: 8rpx;
  849. background-color: #f5f5f5;
  850. flex-shrink: 0;
  851. &:last-child {
  852. margin-right: 0;
  853. }
  854. &.active {
  855. background-color: #007aff;
  856. }
  857. .status-tab-text {
  858. font-size: 26rpx;
  859. color: #666666;
  860. &.active-text {
  861. color: #ffffff;
  862. font-weight: bold;
  863. }
  864. }
  865. }
  866. .list-item {
  867. margin: 3rpx;
  868. padding: 10rpx 10rpx 0rpx 10rpx;
  869. border-bottom: 1px solid #f9f9f9;
  870. }
  871. .item-header {
  872. flex-direction: row;
  873. align-items: center;
  874. justify-content: space-between;
  875. margin-bottom: 10rpx;
  876. }
  877. .item-actions {
  878. display: flex;
  879. align-items: center;
  880. gap: 10rpx;
  881. }
  882. .item-title {
  883. flex: 1;
  884. }
  885. .item-name {
  886. font-size: 28rpx;
  887. color: #333333;
  888. font-weight: bold;
  889. &.status-0 {
  890. color: #ff4d4f;
  891. }
  892. &.status-2 {
  893. color: #999999;
  894. }
  895. }
  896. .item-name-row {
  897. flex-direction: row;
  898. align-items: center;
  899. }
  900. .item-measure {
  901. font-size: 24rpx;
  902. color: #999999;
  903. margin-left: 10rpx;
  904. }
  905. .item-sub-title {
  906. flex: 1;
  907. font-size: 23rpx;
  908. color: #797979;
  909. margin-left: 10rpx;
  910. }
  911. .item-sub-row {
  912. flex-direction: row;
  913. align-items: center;
  914. }
  915. .item-stock {
  916. font-size: 23rpx;
  917. color: #52c41a;
  918. margin-left: 20rpx;
  919. }
  920. .item-measure {
  921. font-size: 23rpx;
  922. color: #999999;
  923. margin-left: 10rpx;
  924. }
  925. .add-btn {
  926. width: 44rpx;
  927. height: 44rpx;
  928. border-radius: 22rpx;
  929. background-color: #165DFF;
  930. color: #FFFFFF;
  931. align-items: center;
  932. justify-content: center;
  933. }
  934. .add-btn.disabled {
  935. background-color: #cccccc;
  936. }
  937. .added-btn {
  938. padding: 8rpx 16rpx;
  939. border-radius: 8rpx;
  940. align-items: center;
  941. justify-content: center;
  942. }
  943. .added-icon {
  944. color: #ff4d4f;
  945. font-size: 24rpx;
  946. }
  947. .detail-link {
  948. font-size: 21rpx;
  949. color: #999999;
  950. }
  951. .material-list {
  952. max-height: 720rpx;
  953. overflow-y: auto;
  954. }
  955. .pagination {
  956. flex-direction: row;
  957. display: flex;
  958. align-items: center;
  959. justify-content: center;
  960. padding: 20rpx;
  961. gap: 30rpx;
  962. }
  963. .page-btn {
  964. padding: 10rpx 20rpx;
  965. margin: 5px;
  966. background-color: #165DFF;
  967. border-radius: 8rpx;
  968. }
  969. .page-btn.disabled {
  970. background-color: #cccccc;
  971. }
  972. .page-btn-text {
  973. color: #FFFFFF;
  974. font-size: 26rpx;
  975. }
  976. .page-info {
  977. font-size: 26rpx;
  978. color: #666666;
  979. }
  980. .selected-list {
  981. padding: 20rpx;
  982. background-color: #ffffff;
  983. min-height: 150rpx;
  984. display: flex;
  985. flex-direction: column;
  986. gap: 15rpx;
  987. }
  988. .empty-tip {
  989. display: flex;
  990. align-items: center;
  991. justify-content: center;
  992. padding: 40rpx;
  993. }
  994. .empty-tip-text {
  995. color: #999999;
  996. font-size: 28rpx;
  997. }
  998. .selected-item {
  999. display: flex;
  1000. flex-direction: row;
  1001. align-items: center;
  1002. background-color: #ffffff;
  1003. border-radius: 10rpx;
  1004. padding: 20rpx;
  1005. border: 1rpx solid #e5e5e5;
  1006. box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.05);
  1007. white-space: nowrap;
  1008. }
  1009. .selected-item-name {
  1010. font-size: 26rpx;
  1011. color: #333333;
  1012. margin-right: 20rpx;
  1013. font-weight: 500;
  1014. // white-space: nowrap;
  1015. max-width: 180rpx;
  1016. // overflow: hidden;
  1017. // text-overflow: ellipsis;
  1018. flex-shrink: 0;
  1019. }
  1020. .selected-item-measure {
  1021. font-size: 24rpx;
  1022. color: #999999;
  1023. margin-right: 20rpx;
  1024. flex-shrink: 0;
  1025. }
  1026. .quantity-control {
  1027. display: flex;
  1028. flex-direction: row;
  1029. align-items: center;
  1030. margin-left: auto;
  1031. margin-right: 20rpx;
  1032. flex-shrink: 0;
  1033. }
  1034. .qty-btn {
  1035. width: 44rpx;
  1036. height: 44rpx;
  1037. background-color: #f5f5f5;
  1038. border-radius: 6rpx;
  1039. align-items: center;
  1040. justify-content: center;
  1041. border: 1rpx solid #e5e5e5;
  1042. }
  1043. .qty-btn-text {
  1044. color: #333333;
  1045. font-size: 28rpx;
  1046. font-weight: bold;
  1047. }
  1048. .quantity-input {
  1049. width: 160rpx;
  1050. height: 44rpx;
  1051. background-color: #f5f5f5;
  1052. border-radius: 6rpx;
  1053. font-size: 24rpx;
  1054. padding: 0 10rpx;
  1055. border: 1rpx solid #e5e5e5;
  1056. text-align: center;
  1057. margin: 0 10rpx;
  1058. }
  1059. .quantity-input:focus {
  1060. outline: none;
  1061. border-color: #007aff;
  1062. background-color: #ffffff;
  1063. }
  1064. .delete-btn {
  1065. width: 40rpx;
  1066. height: 40rpx;
  1067. border-radius: 20rpx;
  1068. background-color: #ff4d4f;
  1069. align-items: center;
  1070. justify-content: center;
  1071. display: flex;
  1072. flex-shrink: 0; // 防止删除按钮被压缩
  1073. }
  1074. .delete-icon {
  1075. color: #ffffff;
  1076. font-size: 30rpx;
  1077. font-weight: bold;
  1078. }
  1079. /* 底部按钮 */
  1080. .bottom-buttons {
  1081. padding: 20rpx;
  1082. background-color: #ffffff;
  1083. border-top: 1rpx solid #e5e5e5;
  1084. position: fixed;
  1085. bottom: 0;
  1086. left: 0;
  1087. right: 0;
  1088. z-index: 100;
  1089. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  1090. }
  1091. .btn-row {
  1092. flex-direction: row;
  1093. display: flex;
  1094. gap: 20rpx;
  1095. }
  1096. .save-btn {
  1097. width: 180rpx;
  1098. height: 80rpx;
  1099. background-color: #f5f5f5;
  1100. color: #333333;
  1101. font-size: 28rpx;
  1102. font-weight: 500;
  1103. border-radius: 20rpx;
  1104. border: 1rpx solid #e5e5e5;
  1105. }
  1106. .confirm-btn {
  1107. flex: 1;
  1108. height: 80rpx;
  1109. background-color: #007aff;
  1110. color: #ffffff;
  1111. font-size: 28rpx;
  1112. font-weight: 600;
  1113. border-radius: 20rpx;
  1114. margin-left: 10rpx;
  1115. border: none;
  1116. box-shadow: 0 2rpx 8rpx rgba(0, 122, 255, 0.3);
  1117. }
  1118. /* 新增物料弹窗样式 */
  1119. .picker-modal {
  1120. position: fixed;
  1121. top: 0;
  1122. left: 0;
  1123. right: 0;
  1124. bottom: 0;
  1125. z-index: 1000;
  1126. display: flex;
  1127. align-items: center;
  1128. justify-content: center;
  1129. }
  1130. .modal-mask {
  1131. position: absolute;
  1132. top: 0;
  1133. left: 0;
  1134. right: 0;
  1135. bottom: 0;
  1136. background-color: rgba(0, 0, 0, 0.5);
  1137. }
  1138. .modal-content {
  1139. position: relative;
  1140. width: 90%;
  1141. // max-width: 500rpx;
  1142. background-color: #ffffff;
  1143. border-radius: 16rpx;
  1144. padding: 30rpx;
  1145. box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
  1146. }
  1147. .modal-header {
  1148. display: flex;
  1149. flex-direction: row;
  1150. justify-content: space-between;
  1151. align-items: center;
  1152. margin-bottom: 30rpx;
  1153. padding-bottom: 20rpx;
  1154. border-bottom: 1rpx solid #e5e5e5;
  1155. }
  1156. .modal-title {
  1157. font-size: 32rpx;
  1158. font-weight: bold;
  1159. color: #333333;
  1160. }
  1161. .modal-close {
  1162. font-size: 28rpx;
  1163. color: #999999;
  1164. padding: 8rpx 16rpx;
  1165. border-radius: 8rpx;
  1166. background-color: #f5f5f5;
  1167. }
  1168. .form-item-input {
  1169. display: flex;
  1170. flex-direction: column;
  1171. margin-bottom: 24rpx;
  1172. }
  1173. .label-picker {
  1174. font-size: 28rpx;
  1175. color: #333333;
  1176. margin-bottom: 12rpx;
  1177. font-weight: 500;
  1178. }
  1179. .view-input-picker {
  1180. position: relative;
  1181. width: 100%;
  1182. }
  1183. .input-picker {
  1184. width: 100%;
  1185. height: 80rpx;
  1186. padding: 0 24rpx;
  1187. background-color: #f5f5f5;
  1188. border-radius: 12rpx;
  1189. font-size: 28rpx;
  1190. color: #333333;
  1191. border: 1rpx solid #e5e5e5;
  1192. }
  1193. .input-picker:focus {
  1194. outline: none;
  1195. border-color: #007aff;
  1196. background-color: #ffffff;
  1197. }
  1198. .form-item-btn {
  1199. margin-top: 30rpx;
  1200. display: flex;
  1201. justify-content: center;
  1202. }
  1203. .btn-primary {
  1204. width: 100%;
  1205. height: 80rpx;
  1206. background-color: #007aff;
  1207. color: #ffffff;
  1208. font-size: 28rpx;
  1209. font-weight: 600;
  1210. border-radius: 12rpx;
  1211. border: none;
  1212. box-shadow: 0 2rpx 8rpx rgba(0, 122, 255, 0.3);
  1213. }
  1214. .add-icon{
  1215. color: #ffffff;
  1216. }
  1217. .measure-list {
  1218. max-height: 500rpx;
  1219. overflow-y: auto;
  1220. }
  1221. .measure-item {
  1222. padding: 24rpx;
  1223. border-bottom: 1rpx solid #f0f0f0;
  1224. }
  1225. .measure-name {
  1226. font-size: 28rpx;
  1227. color: #333333;
  1228. }
  1229. .measure-input-row {
  1230. flex-direction: row;
  1231. align-items: center;
  1232. }
  1233. .measure-input-row .input-picker {
  1234. flex: 1;
  1235. margin-right: 16rpx;
  1236. }
  1237. .pick-btn {
  1238. padding: 16rpx 24rpx;
  1239. background-color: #007aff;
  1240. border-radius: 8rpx;
  1241. }
  1242. .pick-btn-text {
  1243. color: #ffffff;
  1244. font-size: 26rpx;
  1245. }
  1246. /* 图片上传样式 */
  1247. .image-upload-container {
  1248. display: flex;
  1249. flex-direction: row;
  1250. flex-wrap: wrap;
  1251. gap: 16rpx;
  1252. margin-top: 12rpx;
  1253. }
  1254. .uploaded-image {
  1255. position: relative;
  1256. width: 160rpx;
  1257. height: 160rpx;
  1258. border-radius: 8rpx;
  1259. overflow: hidden;
  1260. border: 1rpx solid #e5e5e5;
  1261. }
  1262. .uploaded-image-item {
  1263. width: 100%;
  1264. height: 100%;
  1265. }
  1266. .image-delete-btn {
  1267. position: absolute;
  1268. top: 8rpx;
  1269. right: 8rpx;
  1270. width: 36rpx;
  1271. height: 36rpx;
  1272. border-radius: 18rpx;
  1273. background-color: rgba(0, 0, 0, 0.6);
  1274. display: flex;
  1275. align-items: center;
  1276. justify-content: center;
  1277. }
  1278. .image-delete-icon {
  1279. color: #ffffff;
  1280. font-size: 28rpx;
  1281. font-weight: bold;
  1282. }
  1283. .upload-btn {
  1284. width: 160rpx;
  1285. height: 160rpx;
  1286. border-radius: 8rpx;
  1287. border: 2rpx dashed #e5e5e5;
  1288. display: flex;
  1289. flex-direction: column;
  1290. align-items: center;
  1291. justify-content: center;
  1292. background-color: #fafafa;
  1293. }
  1294. .upload-btn-icon {
  1295. font-size: 48rpx;
  1296. color: #999999;
  1297. margin-bottom: 8rpx;
  1298. }
  1299. .upload-btn-text {
  1300. font-size: 24rpx;
  1301. color: #999999;
  1302. }
  1303. /* 删除确认弹窗样式 */
  1304. .delete-confirm-modal {
  1305. position: fixed;
  1306. top: 0;
  1307. left: 0;
  1308. right: 0;
  1309. bottom: 0;
  1310. z-index: 10000;
  1311. display: flex;
  1312. align-items: center;
  1313. justify-content: center;
  1314. }
  1315. .delete-confirm-mask {
  1316. position: absolute;
  1317. top: 0;
  1318. left: 0;
  1319. right: 0;
  1320. bottom: 0;
  1321. background-color: rgba(0, 0, 0, 0.5);
  1322. }
  1323. .delete-confirm-content {
  1324. position: relative;
  1325. width: 600rpx;
  1326. background-color: #ffffff;
  1327. border-radius: 16rpx;
  1328. padding: 40rpx;
  1329. display: flex;
  1330. flex-direction: column;
  1331. align-items: center;
  1332. }
  1333. .delete-confirm-title {
  1334. font-size: 32rpx;
  1335. font-weight: bold;
  1336. color: #333333;
  1337. margin-bottom: 20rpx;
  1338. }
  1339. .delete-confirm-text {
  1340. font-size: 28rpx;
  1341. color: #666666;
  1342. margin-bottom: 40rpx;
  1343. }
  1344. .delete-confirm-btns {
  1345. display: flex;
  1346. flex-direction: row;
  1347. gap: 40rpx;
  1348. }
  1349. .delete-confirm-cancel {
  1350. padding: 20rpx 60rpx;
  1351. background-color: #f5f5f5;
  1352. border-radius: 12rpx;
  1353. }
  1354. .delete-confirm-cancel-text {
  1355. font-size: 28rpx;
  1356. color: #666666;
  1357. }
  1358. .delete-confirm-ok {
  1359. padding: 20rpx 60rpx;
  1360. background-color: #ff4d4f;
  1361. border-radius: 12rpx;
  1362. }
  1363. .delete-confirm-ok-text {
  1364. font-size: 28rpx;
  1365. color: #ffffff;
  1366. }
  1367. </style>