index.js 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019
  1. import Mock from 'mockjs'
  2. const getIdNumber = (id) => Number(String(id || '').replace(/\D/g, '')) || 0
  3. const sortByIdDesc = (list) => [...list].sort((a, b) => getIdNumber(b.id) - getIdNumber(a.id))
  4. // =============================================
  5. // 登录
  6. // =============================================
  7. Mock.mock(/\/api\/login/, 'post', (options) => {
  8. const { username, password } = JSON.parse(options.body || '{}')
  9. if (!username || !password) {
  10. return { code: 400, data: null, msg: '账号或密码不能为空' }
  11. }
  12. if (username === 'admin' && password === '123456') {
  13. return {
  14. code: 200,
  15. data: { token: 'mock-token-' + Date.now(), username },
  16. msg: '登录成功'
  17. }
  18. }
  19. return { code: 401, data: null, msg: '账号或密码错误' }
  20. })
  21. // =============================================
  22. // 页面管理 - 权益介绍页配置
  23. // =============================================
  24. const homeConfigData = {
  25. premier: {
  26. tabName: '卓越理财',
  27. privilegeImageEnabled: false,
  28. buttons: { unlocked: '进入权益专区', locked: '待解锁' },
  29. header: {
  30. imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=800',
  31. mainTitle: '卓越理财',
  32. subTitle: '为您和您的家人提供尊崇专属礼遇和权益'
  33. },
  34. privileges: [
  35. { imageUrl: '', title: '国际教育', desc: '享专属国际化学校优惠及背景提升项目' },
  36. { imageUrl: '', title: '酒店礼遇', desc: '高端酒店及商场会籍匹配' }
  37. ]
  38. },
  39. elite: {
  40. tabName: '卓越理财·尊尚',
  41. privilegeImageEnabled: false,
  42. buttons: { unlocked: '进入权益专区', locked: '待解锁' },
  43. header: {
  44. imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=800',
  45. mainTitle: '卓越理财·尊尚',
  46. subTitle: '为您和您的家人提供尊崇专属礼遇和权益'
  47. },
  48. privileges: [
  49. { imageUrl: '', title: '国际教育', desc: '享专属国际化学校优惠及背景提升项目' },
  50. { imageUrl: '', title: '酒店礼遇', desc: '高端酒店及商场会籍匹配' }
  51. ]
  52. },
  53. global: {
  54. tabName: '环球私人银行',
  55. privilegeImageEnabled: false,
  56. buttons: { unlocked: '进入权益专区', locked: '待解锁' },
  57. header: {
  58. imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=800',
  59. mainTitle: '环球私人银行',
  60. subTitle: '为您和您的家人提供尊崇专属礼遇和权益'
  61. },
  62. privileges: [
  63. { imageUrl: '', title: '国际教育', desc: '享专属国际化学校优惠及背景提升项目' },
  64. { imageUrl: '', title: '酒店礼遇', desc: '高端酒店及商场会籍匹配' }
  65. ]
  66. }
  67. }
  68. let homeStatus = 'published'
  69. Mock.mock(/\/api\/page-config\/home/, 'get', () => {
  70. return { code: 200, data: { config: homeConfigData, status: homeStatus }, msg: 'ok' }
  71. })
  72. Mock.mock(/\/api\/page-config\/home/, 'put', (options) => {
  73. const body = JSON.parse(options.body)
  74. Object.assign(homeConfigData, body.config || {})
  75. homeStatus = body.status || homeStatus
  76. return { code: 200, data: null, msg: '保存成功' }
  77. })
  78. // =============================================
  79. // 页面管理 - 权益首页配置
  80. // =============================================
  81. const zoneConfigData = {
  82. identityModule: {
  83. advance: {
  84. bgImage: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=800',
  85. showAvgBalance: true,
  86. avgBalanceName: '账户月内日均余额',
  87. avgBalanceRule: '<p>1.指从当月第一天起到截止日期为止,同一客户号码下所有账户月内日均总余额。</p><p>2. 计算公式=当月第一天到截止日期的累计账户总余额当月已过的自然天数。</p>'
  88. },
  89. premier: {
  90. bgImage: 'https://images.unsplash.com/photo-1583847268964-b28dc8f51f92?w=800',
  91. showAvgBalance: true,
  92. avgBalanceName: '账户月内日均余额',
  93. identityTipContent: '<p>卓越理财客户身份由客户 MSC Group 自动匹配展示。</p>',
  94. avgBalanceRule: '<p>1.指从当月第一天起到截止日期为止,同一客户号码下所有账户月内日均总余额。</p><p>2. 计算公式=当月第一天到截止日期的累计账户总余额当月已过的自然天数。</p>'
  95. },
  96. premierElite: {
  97. bgImage: 'https://images.unsplash.com/photo-1557682250-33bd709cbe85?w=800',
  98. showAvgBalance: true,
  99. avgBalanceName: '账户月内日均余额',
  100. identityTipContent: '<p>卓越理财·尊尚客户身份由客户 MSC Group 自动匹配展示。</p>',
  101. avgBalanceRule: '<p>1.指从当月第一天起到截止日期为止,同一客户号码下所有账户月内日均总余额。</p><p>2. 计算公式=当月第一天到截止日期的累计账户总余额当月已过的自然天数。</p>'
  102. },
  103. privateBank: {
  104. bgImage: 'https://images.unsplash.com/photo-1492684223066-81342ee5ff30?w=800',
  105. identityTipContent: '<p>环球私人银行客户身份由客户 MSC Group 自动匹配展示。</p>'
  106. }
  107. },
  108. adCarousel: [{
  109. imageUrl: 'https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?w=800',
  110. linkH5: '', linkApp: '', wechatAppId: '', wechatPath: '',
  111. title: '汇丰卓越理财新户达标礼',
  112. subtitle: '报名参加汇丰活动,体验更多精彩礼遇',
  113. tags: ['t1', 't2']
  114. }],
  115. exchangeConfig: {
  116. items: [
  117. { imageUrl: 'https://images.unsplash.com/photo-1436491865332-7a61a109cc05?w=200', title: '航空里程', link: 'https://test.hsbc.com' },
  118. { imageUrl: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=200', title: '贵宾厅', link: 'https://test.hsbc.com' }
  119. ]
  120. },
  121. ruleModal: {
  122. title: '积分规则',
  123. content: '<p>本积分使用规则为汇丰银行(中国)有限公司...</p>'
  124. }
  125. }
  126. let zoneStatus = 'published'
  127. const availableTags = Array.from({ length: 8 }, (_, i) => ({ id: `t${i + 1}`, name: `人群标签${i + 1}` }))
  128. Mock.mock(/\/api\/page-config\/zone/, 'get', () => {
  129. return { code: 200, data: { config: zoneConfigData, status: zoneStatus, availableTags }, msg: 'ok' }
  130. })
  131. Mock.mock(/\/api\/page-config\/zone/, 'put', (options) => {
  132. const body = JSON.parse(options.body)
  133. Object.assign(zoneConfigData, body.config || {})
  134. zoneStatus = body.status || zoneStatus
  135. return { code: 200, data: null, msg: '保存成功' }
  136. })
  137. // =============================================
  138. // 页面管理 - 组装页
  139. // =============================================
  140. let assemblyIdCounter = 2
  141. const assemblyList = [
  142. {
  143. id: '1', name: '双十一迎新专属活动页', status: 'published',
  144. createTime: '2025-10-01 10:00:00', updateTime: '2025-10-15 14:30:00',
  145. shareConfig: {
  146. enabled: true,
  147. thumbnail: '',
  148. title: '',
  149. desc: ''
  150. },
  151. blocks: [
  152. { type: 'image', id: 'b_init1', data: { imageUrl: 'https://images.unsplash.com/photo-1557683316-973673baf926?w=800' } },
  153. { type: 'privilege', id: 'b_init2', data: { title: '专属权益', items: [{ id: '1', title: '机场贵宾厅服务', desc: '全年免费使用6次指定机场贵宾厅' }, { id: '2', title: '境内礼宾车', desc: '免费2次接送' }] } },
  154. { type: 'richtext', id: 'b_init3', data: { content: '<p>欢迎参与双十一专属福利活动...</p>' } },
  155. { type: 'buttonGroup', id: 'b_init4', data: { buttons: [{ text: '立即参与', link: '/join', style: 'primary', actionType: 'link', dialog: { title: '', desc: '', confirmText: '确认', confirmLink: '' } }] } }
  156. ]
  157. },
  158. {
  159. id: '2', name: '私行高净值沙龙引导页', status: 'draft',
  160. createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00',
  161. shareConfig: {
  162. enabled: true,
  163. thumbnail: '',
  164. title: '',
  165. desc: ''
  166. },
  167. blocks: [
  168. { type: 'image', id: 'b_init5', data: { imageUrl: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=800' } },
  169. { type: 'privilege', id: 'b_init6', data: { title: '尊享体验', items: [{ id: '1', title: '高尔夫权益', desc: 'VIP高尔夫俱乐部免会籍预约' }] } }
  170. ]
  171. }
  172. ]
  173. Mock.mock(/\/api\/assembly\/list/, 'get', (options) => {
  174. const url = new URL(options.url, 'http://localhost')
  175. const id = url.searchParams.get('id') || ''
  176. const name = url.searchParams.get('name') || ''
  177. let filtered = assemblyList
  178. if (id) filtered = filtered.filter(a => a.id.includes(id))
  179. if (name) filtered = filtered.filter(a => a.name.includes(name))
  180. return { code: 200, data: sortByIdDesc(filtered), msg: 'ok' }
  181. })
  182. Mock.mock(/\/api\/assembly\/detail/, 'get', (options) => {
  183. const url = new URL(options.url, 'http://localhost')
  184. const id = url.searchParams.get('id')
  185. const item = assemblyList.find(a => a.id === id)
  186. return item
  187. ? { code: 200, data: item, msg: 'ok' }
  188. : { code: 404, data: null, msg: '未找到' }
  189. })
  190. Mock.mock(/\/api\/assembly\/save/, 'post', (options) => {
  191. const body = JSON.parse(options.body)
  192. const now = new Date().toLocaleString('zh-CN', { hour12: false })
  193. if (body.id) {
  194. const idx = assemblyList.findIndex(a => a.id === body.id)
  195. if (idx >= 0) {
  196. assemblyList[idx] = { ...assemblyList[idx], ...body, updateTime: now }
  197. }
  198. } else {
  199. body.id = String(++assemblyIdCounter)
  200. body.createTime = now
  201. body.updateTime = now
  202. assemblyList.unshift(body)
  203. }
  204. return { code: 200, data: { id: body.id }, msg: '保存成功' }
  205. })
  206. Mock.mock(/\/api\/assembly\/delete/, 'post', (options) => {
  207. const body = JSON.parse(options.body)
  208. const idx = assemblyList.findIndex(a => a.id === body.id)
  209. if (idx >= 0) assemblyList.splice(idx, 1)
  210. return { code: 200, data: null, msg: '删除成功' }
  211. })
  212. // =============================================
  213. // 渠道管理 - Channel
  214. // =============================================
  215. const channelDict = {
  216. 'Human-assisted': {
  217. '汇丰中国客户服务号': ['汇丰中国客户服务号'],
  218. 'MGM': ['MGM - Mortgage', 'MGM - Premier'],
  219. 'CCSS': ['CCSS SMS'],
  220. 'Referral': ['GPB Referral', 'HK referral', 'Non WPB staff Referral', 'Pinnacle Referral', 'RBB Refferal'],
  221. 'New staff': ['New staff'],
  222. 'IBC': ['IBC referral staff code'],
  223. 'Group Acct': ['HTC', 'NIVIDA']
  224. },
  225. 'Unassisted': {
  226. '汇丰中国APP': ['Mobile X'],
  227. '汇丰中国官网': ['PWS site'],
  228. '汇丰中国小程序': ['汇丰中国小程序'],
  229. '汇丰中国订阅号': ['汇丰中国订阅号', '汇丰中国订阅号推文', '推文尾部入口'],
  230. 'Chatbot': ['Chatbot'],
  231. 'SMS': ['SMS'],
  232. 'Paid Media': ['汇丰中国订阅号推文', 'others'],
  233. 'Others': ['others', 'River APP']
  234. }
  235. }
  236. let channelIdCounter = 9
  237. const channelRows = [
  238. { id: 'CH0001', type: 'Human-assisted', category: '汇丰中国客户服务号', name: '汇丰中国客户服务号', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
  239. { id: 'CH0002', type: 'Human-assisted', category: 'MGM', name: 'MGM - Mortgage', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
  240. { id: 'CH0003', type: 'Human-assisted', category: 'MGM', name: 'MGM - Premier', subName: '双十一特惠', owner: '张经理', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-21 14:30:00' },
  241. { id: 'CH0004', type: 'Human-assisted', category: 'CCSS', name: 'CCSS SMS', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
  242. { id: 'CH0005', type: 'Human-assisted', category: 'Referral', name: 'GPB Referral', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
  243. { id: 'CH0006', type: 'Unassisted', category: '汇丰中国APP', name: 'Mobile X', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
  244. { id: 'CH0007', type: 'Unassisted', category: '汇丰中国官网', name: 'PWS site', subName: '私行沙龙引导', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
  245. { id: 'CH0008', type: 'Unassisted', category: '汇丰中国订阅号', name: '汇丰中国订阅号', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' },
  246. { id: 'CH0009', type: 'Unassisted', category: '汇丰中国订阅号', name: '推文尾部入口', subName: '', owner: '', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' }
  247. ]
  248. Mock.mock(/\/api\/channel\/dict/, 'get', () => {
  249. return { code: 200, data: channelDict, msg: 'ok' }
  250. })
  251. Mock.mock(/\/api\/channel\/dict\/save/, 'post', (options) => {
  252. const body = JSON.parse(options.body)
  253. const { target, type, category, oldValue, newValue, mode } = body
  254. if (target === 'type') {
  255. if (mode === 'add' && channelDict[newValue]) return { code: 400, data: null, msg: 'Channel 类型已存在,不能重复新增' }
  256. if (mode === 'edit' && oldValue !== newValue && channelDict[newValue]) return { code: 400, data: null, msg: 'Channel 类型已存在,不能修改为重复值' }
  257. if (mode === 'add') { channelDict[newValue] = {} }
  258. else { channelDict[newValue] = channelDict[oldValue]; delete channelDict[oldValue]; channelRows.forEach(r => { if (r.type === oldValue) r.type = newValue }) }
  259. } else if (target === 'category') {
  260. if (mode === 'add' && channelDict[type][newValue]) return { code: 400, data: null, msg: 'Channel 类别已存在,不能重复新增' }
  261. if (mode === 'edit' && oldValue !== newValue && channelDict[type][newValue]) return { code: 400, data: null, msg: 'Channel 类别已存在,不能修改为重复值' }
  262. if (mode === 'add') { channelDict[type][newValue] = [] }
  263. else { channelDict[type][newValue] = channelDict[type][oldValue]; delete channelDict[type][oldValue]; channelRows.forEach(r => { if (r.type === type && r.category === oldValue) r.category = newValue }) }
  264. } else if (target === 'name') {
  265. const list = channelDict[type][category]
  266. if (mode === 'add' && list.includes(newValue)) return { code: 400, data: null, msg: 'Channel 名称已存在,不能重复新增' }
  267. if (mode === 'edit' && oldValue !== newValue && list.includes(newValue)) return { code: 400, data: null, msg: 'Channel 名称已存在,不能修改为重复值' }
  268. if (mode === 'add') { list.push(newValue) }
  269. else { const idx = list.indexOf(oldValue); if (idx >= 0) list[idx] = newValue; channelRows.forEach(r => { if (r.type === type && r.category === category && r.name === oldValue) r.name = newValue }) }
  270. }
  271. return { code: 200, data: channelDict, msg: '保存成功' }
  272. })
  273. Mock.mock(/\/api\/channel\/list/, 'get', (options) => {
  274. const url = new URL(options.url, 'http://localhost')
  275. const sId = (url.searchParams.get('id') || '').toLowerCase()
  276. const sType = url.searchParams.get('type') || ''
  277. const sCategory = (url.searchParams.get('category') || '').toLowerCase()
  278. const sName = (url.searchParams.get('name') || '').toLowerCase()
  279. const sSubName = (url.searchParams.get('subName') || '').toLowerCase()
  280. const sOwner = (url.searchParams.get('owner') || '').toLowerCase()
  281. let filtered = channelRows
  282. if (sId) filtered = filtered.filter(c => c.id.toLowerCase().includes(sId))
  283. if (sType) filtered = filtered.filter(c => c.type === sType)
  284. if (sCategory) filtered = filtered.filter(c => c.category.toLowerCase().includes(sCategory))
  285. if (sName) filtered = filtered.filter(c => c.name.toLowerCase().includes(sName))
  286. if (sSubName) filtered = filtered.filter(c => c.subName.toLowerCase().includes(sSubName))
  287. if (sOwner) filtered = filtered.filter(c => c.owner.toLowerCase().includes(sOwner))
  288. return { code: 200, data: filtered, msg: 'ok' }
  289. })
  290. Mock.mock(/\/api\/channel\/save/, 'post', (options) => {
  291. const body = JSON.parse(options.body)
  292. const now = new Date().toLocaleString('zh-CN', { hour12: false })
  293. if (body.id) {
  294. const idx = channelRows.findIndex(c => c.id === body.id)
  295. if (idx >= 0) channelRows[idx] = { ...channelRows[idx], ...body, updateTime: now }
  296. } else {
  297. body.id = 'CH' + String(++channelIdCounter).padStart(4, '0')
  298. body.createTime = now
  299. body.updateTime = now
  300. channelRows.unshift(body)
  301. }
  302. return { code: 200, data: { id: body.id }, msg: '保存成功' }
  303. })
  304. Mock.mock(/\/api\/channel\/delete/, 'post', (options) => {
  305. const body = JSON.parse(options.body)
  306. const idx = channelRows.findIndex(c => c.id === body.id)
  307. if (idx >= 0) channelRows.splice(idx, 1)
  308. return { code: 200, data: null, msg: '删除成功' }
  309. })
  310. // =============================================
  311. // 渠道管理 - Campaign
  312. // =============================================
  313. let campaignIdCounter = 9002
  314. const campaignRows = [
  315. { id: 'CP9001', name: 'Q3 大客户回馈', owner: 'Market Team', staff: '王专员', createTime: '2025-10-01 10:00:00', updateTime: '2025-10-15 14:30:00' },
  316. { id: 'CP9002', name: '新春开户活动', owner: 'Retail Team', staff: '李专员', createTime: '2025-11-20 09:15:00', updateTime: '2025-11-20 09:15:00' }
  317. ]
  318. Mock.mock(/\/api\/campaign\/list/, 'get', (options) => {
  319. const url = new URL(options.url, 'http://localhost')
  320. const sId = (url.searchParams.get('id') || '').toLowerCase()
  321. const sName = (url.searchParams.get('name') || '').toLowerCase()
  322. const sOwner = (url.searchParams.get('owner') || '').toLowerCase()
  323. const sStaff = (url.searchParams.get('staff') || '').toLowerCase()
  324. let filtered = campaignRows
  325. if (sId) filtered = filtered.filter(c => c.id.toLowerCase().includes(sId))
  326. if (sName) filtered = filtered.filter(c => c.name.toLowerCase().includes(sName))
  327. if (sOwner) filtered = filtered.filter(c => c.owner.toLowerCase().includes(sOwner))
  328. if (sStaff) filtered = filtered.filter(c => c.staff.toLowerCase().includes(sStaff))
  329. return { code: 200, data: filtered, msg: 'ok' }
  330. })
  331. Mock.mock(/\/api\/campaign\/save/, 'post', (options) => {
  332. const body = JSON.parse(options.body)
  333. const now = new Date().toLocaleString('zh-CN', { hour12: false })
  334. if (body.id) {
  335. const idx = campaignRows.findIndex(c => c.id === body.id)
  336. if (idx >= 0) campaignRows[idx] = { ...campaignRows[idx], ...body, updateTime: now }
  337. } else {
  338. body.id = 'CP' + (++campaignIdCounter)
  339. body.createTime = now
  340. body.updateTime = now
  341. campaignRows.unshift(body)
  342. }
  343. return { code: 200, data: { id: body.id }, msg: '保存成功' }
  344. })
  345. Mock.mock(/\/api\/campaign\/delete/, 'post', (options) => {
  346. const body = JSON.parse(options.body)
  347. const idx = campaignRows.findIndex(c => c.id === body.id)
  348. if (idx >= 0) campaignRows.splice(idx, 1)
  349. return { code: 200, data: null, msg: '删除成功' }
  350. })
  351. // =============================================
  352. // 借记卡权益 + 审核中心
  353. // =============================================
  354. // 商品状态:未上架 / 已上架
  355. // 审核状态:草稿 / 上架待审核 / 下架待审核 / 审核通过
  356. // 合法组合(5 种):
  357. // 未上架+草稿 = 草稿
  358. // 未上架+上架待审核 = 新增待审核
  359. // 未上架+审核通过 = 已下架
  360. // 已上架+审核通过 = 已上架
  361. // 已上架+下架待审核 = 已上架·下架待审核
  362. const equityList = [
  363. {
  364. productId: 'Q100001',
  365. equityName: '权益A',
  366. source: '银行自有',
  367. isPlatformExchange: '是',
  368. jumpMethod: '-',
  369. jumpLink: '-',
  370. exchangeMethod: '权益平台',
  371. productType: '券码',
  372. skuNo: 'C0000001000000101',
  373. stockCount: 5000,
  374. merchantName: '商户A',
  375. creator: '李运营',
  376. createTime: '2026-04-10 09:32:15',
  377. reviewer: '王五',
  378. reviewTime: '2026-04-15 10:30:00',
  379. shelfStatus: '已上架',
  380. auditStatus: '审核通过',
  381. showOnHome: '显示',
  382. showOnAssembly: '不显示',
  383. assemblyPages: []
  384. },
  385. {
  386. productId: 'Q100002',
  387. equityName: '权益B',
  388. source: '第三方',
  389. isPlatformExchange: '否',
  390. jumpMethod: '没有',
  391. jumpLink: '太古地产',
  392. exchangeMethod: '第三方(列表跳转)',
  393. productType: '',
  394. skuNo: '',
  395. merchantName: '商户B',
  396. creator: '王运营',
  397. createTime: '2026-04-12 14:08:42',
  398. reviewer: '',
  399. reviewTime: '',
  400. shelfStatus: '未上架',
  401. auditStatus: '上架待审核',
  402. showOnHome: '显示',
  403. showOnAssembly: '不显示',
  404. assemblyPages: []
  405. },
  406. {
  407. productId: 'Q100003',
  408. equityName: '权益C',
  409. source: '第三方',
  410. isPlatformExchange: '否',
  411. jumpMethod: '有',
  412. jumpLink: '龙腾',
  413. exchangeMethod: '第三方(详情页跳转)',
  414. productType: '',
  415. skuNo: '',
  416. merchantName: '商户C',
  417. creator: '李运营',
  418. createTime: '2026-04-08 11:20:05',
  419. reviewer: '王五',
  420. reviewTime: '2026-04-14 16:45:00',
  421. shelfStatus: '已上架',
  422. auditStatus: '审核通过',
  423. showOnHome: '显示',
  424. showOnAssembly: '不显示',
  425. assemblyPages: []
  426. },
  427. {
  428. productId: 'Q100004',
  429. equityName: '权益D',
  430. source: '第三方',
  431. isPlatformExchange: '否',
  432. jumpMethod: '有',
  433. jumpLink: 'h5链接',
  434. exchangeMethod: '第三方(详情页跳转)',
  435. productType: '',
  436. skuNo: '',
  437. merchantName: '商户D',
  438. creator: '陈运营',
  439. createTime: '2026-04-05 15:50:18',
  440. reviewer: '赵六',
  441. reviewTime: '2026-04-13 09:15:00',
  442. shelfStatus: '已上架',
  443. auditStatus: '下架待审核',
  444. showOnHome: '显示',
  445. showOnAssembly: '不显示',
  446. assemblyPages: []
  447. },
  448. {
  449. productId: 'Q100005',
  450. equityName: '权益E',
  451. source: '银行自有',
  452. isPlatformExchange: '是',
  453. jumpMethod: '-',
  454. jumpLink: '-',
  455. exchangeMethod: '权益平台',
  456. productType: '券码',
  457. skuNo: 'C0000001000000501',
  458. stockCount: 3000,
  459. merchantName: '商户E',
  460. creator: '王运营',
  461. createTime: '2026-03-28 09:00:11',
  462. reviewer: '钱七',
  463. reviewTime: '2026-04-12 11:20:00',
  464. shelfStatus: '未上架',
  465. auditStatus: '审核通过',
  466. showOnHome: '显示',
  467. showOnAssembly: '不显示',
  468. assemblyPages: []
  469. },
  470. {
  471. productId: 'Q100006',
  472. equityName: '权益F(草稿)',
  473. source: '银行自有',
  474. isPlatformExchange: '是',
  475. jumpMethod: '-',
  476. jumpLink: '-',
  477. exchangeMethod: '权益平台',
  478. productType: '券码',
  479. skuNo: 'C0000001000000601',
  480. stockCount: 1000,
  481. merchantName: '商户F',
  482. creator: '李运营',
  483. createTime: '2026-05-16 10:00:00',
  484. reviewer: '',
  485. reviewTime: '',
  486. shelfStatus: '未上架',
  487. auditStatus: '草稿',
  488. showOnHome: '不显示',
  489. showOnAssembly: '不显示',
  490. assemblyPages: []
  491. },
  492. {
  493. productId: 'Q100007',
  494. equityName: '微信立减金 5元(满10减5)',
  495. source: '荣数商品',
  496. isPlatformExchange: '是',
  497. jumpMethod: '-',
  498. jumpLink: '-',
  499. exchangeMethod: '权益平台',
  500. productType: '微信立减金',
  501. skuNo: 'V0000003900000801',
  502. rongshuProduct: '微信立减金 5元(满10减5)',
  503. merchantName: '微信支付',
  504. stockCount: 2000,
  505. creator: '张三',
  506. createTime: '2026-05-20 10:00:00',
  507. reviewer: '李四',
  508. reviewTime: '2026-05-22 15:30:00',
  509. shelfStatus: '已上架',
  510. auditStatus: '审核通过',
  511. showOnHome: '显示',
  512. showOnAssembly: '不显示',
  513. assemblyPages: []
  514. },
  515. {
  516. productId: 'Q100008',
  517. equityName: '微信立减金 50元(满200减50)',
  518. source: '荣数商品',
  519. isPlatformExchange: '是',
  520. jumpMethod: '-',
  521. jumpLink: '-',
  522. exchangeMethod: '权益平台',
  523. productType: '微信立减金',
  524. skuNo: 'V0000003900000807',
  525. rongshuProduct: '微信立减金 50元(满200减50)',
  526. merchantName: '微信支付',
  527. stockCount: 400,
  528. creator: '张三',
  529. createTime: '2026-05-18 09:30:00',
  530. reviewer: '',
  531. reviewTime: '',
  532. shelfStatus: '未上架',
  533. auditStatus: '上架待审核',
  534. showOnHome: '显示',
  535. showOnAssembly: '显示',
  536. assemblyPages: ['组装页1']
  537. }
  538. ]
  539. let equityIdCounter = 100008
  540. const auditList = [
  541. {
  542. id: 'AR202605150001',
  543. auditType: 'new',
  544. bizModule: 'equity_product',
  545. bizModuleName: '借记卡权益',
  546. bizId: 'Q100002',
  547. bizName: '权益B',
  548. snapshot: {
  549. equityName: '权益B',
  550. source: '第三方',
  551. isPlatformExchange: '否',
  552. jumpMethod: '没有',
  553. jumpLink: '太古地产',
  554. productType: '',
  555. skuNo: '',
  556. merchantName: '商户B',
  557. subTitle: '外部列表页副标题'
  558. },
  559. submitReason: '新建第三方权益,链接已对齐',
  560. submitterId: 'u001',
  561. submitterName: '王运营',
  562. submitTime: '2026-05-15 10:00:00',
  563. auditorId: null,
  564. auditorName: '',
  565. auditTime: '',
  566. auditStatus: 'pending',
  567. auditComment: ''
  568. },
  569. {
  570. id: 'AR202605170001',
  571. auditType: 'offshelf',
  572. bizModule: 'equity_product',
  573. bizModuleName: '借记卡权益',
  574. bizId: 'Q100004',
  575. bizName: '权益D',
  576. snapshot: {
  577. equityName: '权益D',
  578. source: '第三方',
  579. isPlatformExchange: '否',
  580. jumpMethod: '有',
  581. jumpLink: 'h5链接',
  582. productType: '',
  583. skuNo: '',
  584. merchantName: '商户D',
  585. subTitle: '外部自定义链接副标题'
  586. },
  587. submitReason: '业务合作到期,申请下架',
  588. submitterId: 'u003',
  589. submitterName: '陈运营',
  590. submitTime: '2026-05-17 09:00:00',
  591. auditorId: null,
  592. auditorName: '',
  593. auditTime: '',
  594. auditStatus: 'pending',
  595. auditComment: ''
  596. },
  597. {
  598. id: 'AR202605120001',
  599. auditType: 'new',
  600. bizModule: 'equity_product',
  601. bizModuleName: '借记卡权益',
  602. bizId: 'Q100001',
  603. bizName: '权益A',
  604. snapshot: {
  605. equityName: '权益A',
  606. source: '银行自有',
  607. isPlatformExchange: '是',
  608. jumpMethod: '-',
  609. jumpLink: '-',
  610. productType: '券码',
  611. skuNo: 'C0000001000000101',
  612. stockCount: 5000,
  613. merchantName: '商户A',
  614. subTitle: '副标题示例'
  615. },
  616. submitReason: '',
  617. submitterId: 'u001',
  618. submitterName: '李运营',
  619. submitTime: '2026-04-10 09:32:15',
  620. auditorId: 'u099',
  621. auditorName: '王五',
  622. auditTime: '2026-04-15 10:30:00',
  623. auditStatus: 'approved',
  624. auditComment: '内容合规,准予上架'
  625. }
  626. ]
  627. let auditIdCounter = 4
  628. function nowStr() {
  629. return new Date().toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-')
  630. }
  631. function nextAuditId() {
  632. auditIdCounter += 1
  633. const d = new Date()
  634. const ymd = d.getFullYear().toString() + String(d.getMonth() + 1).padStart(2, '0') + String(d.getDate()).padStart(2, '0')
  635. return 'AR' + ymd + String(auditIdCounter).padStart(4, '0')
  636. }
  637. function snapshotOf(equity, couponBatchId) {
  638. return {
  639. equityName: equity.equityName,
  640. source: equity.source,
  641. isPlatformExchange: equity.isPlatformExchange || (equity.source === '第三方' ? '否' : '是'),
  642. productType: equity.isPlatformExchange === '否' ? '' : (equity.productType || '券码'),
  643. jumpMethod: equity.jumpMethod,
  644. jumpLink: equity.jumpLink,
  645. skuNo: equity.skuNo,
  646. merchantName: equity.merchantName,
  647. subTitle: equity.subTitle || '',
  648. couponBatchId: couponBatchId !== undefined ? (couponBatchId || '') : (equity.couponBatchId || '')
  649. }
  650. }
  651. // 借记卡权益 - 列表
  652. Mock.mock(/\/api\/equity\/list/, 'get', (options) => {
  653. const url = new URL(options.url, 'http://localhost')
  654. const equityName = url.searchParams.get('equityName') || ''
  655. const productId = url.searchParams.get('productId') || ''
  656. const shelfStatus = url.searchParams.get('shelfStatus') || ''
  657. const auditStatus = url.searchParams.get('auditStatus') || ''
  658. let filtered = equityList
  659. if (equityName) filtered = filtered.filter(e => e.equityName.includes(equityName))
  660. if (productId) filtered = filtered.filter(e => e.productId.includes(productId))
  661. if (shelfStatus) filtered = filtered.filter(e => e.shelfStatus === shelfStatus)
  662. if (auditStatus) filtered = filtered.filter(e => e.auditStatus === auditStatus)
  663. return { code: 200, data: filtered, msg: 'ok' }
  664. })
  665. // 借记卡权益 - 详情
  666. Mock.mock(/\/api\/equity\/detail/, 'get', (options) => {
  667. const url = new URL(options.url, 'http://localhost')
  668. const id = url.searchParams.get('id')
  669. const item = equityList.find(e => e.productId === id)
  670. return item
  671. ? { code: 200, data: item, msg: 'ok' }
  672. : { code: 404, data: null, msg: '权益不存在' }
  673. })
  674. // 保存草稿(新建或修改草稿)
  675. Mock.mock(/\/api\/equity\/save-draft/, 'post', (options) => {
  676. const body = JSON.parse(options.body)
  677. if (body.productId) {
  678. // 修改草稿
  679. const idx = equityList.findIndex(e => e.productId === body.productId)
  680. if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
  681. const cur = equityList[idx]
  682. if (cur.auditStatus !== '草稿') {
  683. return { code: 400, data: null, msg: '仅草稿状态可保存为草稿' }
  684. }
  685. equityList[idx] = { ...cur, ...body, shelfStatus: '未上架', auditStatus: '草稿' }
  686. return { code: 200, data: { productId: cur.productId }, msg: '草稿已保存' }
  687. }
  688. // 新建草稿
  689. equityIdCounter += 1
  690. const newId = 'Q' + String(equityIdCounter)
  691. const newRow = {
  692. productId: newId,
  693. equityName: body.equityName || '未命名权益',
  694. source: body.source || '银行自有',
  695. isPlatformExchange: body.isPlatformExchange || '是',
  696. jumpMethod: body.jumpMethod || '-',
  697. jumpLink: body.jumpLink || '-',
  698. exchangeMethod: body.exchangeMethod || '权益平台',
  699. productType: body.isPlatformExchange === '否' ? '' : (body.productType || '券码'),
  700. skuNo: body.skuNo || '',
  701. rongshuProduct: body.rongshuProduct || '',
  702. stockCount: body.stockCount ?? null,
  703. merchantName: body.merchantName || '',
  704. creator: body.creator || '李运营',
  705. createTime: nowStr(),
  706. reviewer: '',
  707. reviewTime: '',
  708. shelfStatus: '未上架',
  709. auditStatus: '草稿',
  710. showOnHome: body.showOnHome || '不显示',
  711. showOnAssembly: body.showOnAssembly || '不显示',
  712. assemblyPages: body.assemblyPages || [],
  713. subTitle: body.subTitle || '',
  714. couponBatchId: body.couponBatchId || ''
  715. }
  716. equityList.unshift(newRow)
  717. return { code: 200, data: { productId: newId }, msg: '草稿已保存' }
  718. })
  719. // 提交审核(新增上架审核 / 已上架编辑提审 / 已下架重新提审)
  720. Mock.mock(/\/api\/equity\/submit/, 'post', (options) => {
  721. const body = JSON.parse(options.body)
  722. let cur
  723. let idx = -1
  724. if (body.productId) {
  725. idx = equityList.findIndex(e => e.productId === body.productId)
  726. if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
  727. cur = equityList[idx]
  728. }
  729. const submitReason = body.submitReason || ''
  730. const submitter = body.submitterName || '李运营'
  731. // 新建 + 直接提审
  732. if (!cur) {
  733. equityIdCounter += 1
  734. const newId = 'Q' + String(equityIdCounter)
  735. const newRow = {
  736. productId: newId,
  737. equityName: body.equityName || '未命名权益',
  738. source: body.source || '银行自有',
  739. isPlatformExchange: body.isPlatformExchange || '是',
  740. jumpMethod: body.jumpMethod || '-',
  741. jumpLink: body.jumpLink || '-',
  742. exchangeMethod: body.exchangeMethod || '权益平台',
  743. productType: body.isPlatformExchange === '否' ? '' : (body.productType || '券码'),
  744. skuNo: body.skuNo || '',
  745. rongshuProduct: body.rongshuProduct || '',
  746. stockCount: body.stockCount ?? null,
  747. merchantName: body.merchantName || '',
  748. creator: submitter,
  749. createTime: nowStr(),
  750. reviewer: '',
  751. reviewTime: '',
  752. shelfStatus: '未上架',
  753. auditStatus: '上架待审核',
  754. showOnHome: body.showOnHome || '不显示',
  755. showOnAssembly: body.showOnAssembly || '不显示',
  756. assemblyPages: body.assemblyPages || [],
  757. subTitle: body.subTitle || '',
  758. couponBatchId: body.couponBatchId || ''
  759. }
  760. equityList.unshift(newRow)
  761. auditList.unshift({
  762. id: nextAuditId(),
  763. auditType: 'new',
  764. bizModule: 'equity_product',
  765. bizModuleName: '借记卡权益',
  766. bizId: newId,
  767. bizName: newRow.equityName,
  768. snapshot: snapshotOf(newRow),
  769. submitReason,
  770. submitterId: 'u001',
  771. submitterName: submitter,
  772. submitTime: nowStr(),
  773. auditorId: null,
  774. auditorName: '',
  775. auditTime: '',
  776. auditStatus: 'pending',
  777. auditComment: ''
  778. })
  779. return { code: 200, data: { productId: newId }, msg: '已提交审核' }
  780. }
  781. // 草稿 → 上架待审核(new)
  782. if (cur.shelfStatus === '未上架' && cur.auditStatus === '草稿') {
  783. const merged = { ...cur, ...body }
  784. equityList[idx] = { ...merged, shelfStatus: '未上架', auditStatus: '上架待审核' }
  785. auditList.unshift({
  786. id: nextAuditId(),
  787. auditType: 'new',
  788. bizModule: 'equity_product',
  789. bizModuleName: '借记卡权益',
  790. bizId: cur.productId,
  791. bizName: equityList[idx].equityName,
  792. snapshot: snapshotOf(equityList[idx]),
  793. submitReason,
  794. submitterId: 'u001',
  795. submitterName: submitter,
  796. submitTime: nowStr(),
  797. auditorId: null,
  798. auditorName: '',
  799. auditTime: '',
  800. auditStatus: 'pending',
  801. auditComment: ''
  802. })
  803. return { code: 200, data: { productId: cur.productId }, msg: '已提交审核' }
  804. }
  805. // 已下架 → 重新上架(也是 new)
  806. if (cur.shelfStatus === '未上架' && cur.auditStatus === '审核通过') {
  807. equityList[idx] = { ...cur, ...body, shelfStatus: '未上架', auditStatus: '上架待审核' }
  808. auditList.unshift({
  809. id: nextAuditId(),
  810. auditType: 'new',
  811. bizModule: 'equity_product',
  812. bizModuleName: '借记卡权益',
  813. bizId: cur.productId,
  814. bizName: equityList[idx].equityName,
  815. snapshot: snapshotOf(equityList[idx]),
  816. submitReason,
  817. submitterId: 'u001',
  818. submitterName: submitter,
  819. submitTime: nowStr(),
  820. auditorId: null,
  821. auditorName: '',
  822. auditTime: '',
  823. auditStatus: 'pending',
  824. auditComment: ''
  825. })
  826. return { code: 200, data: { productId: cur.productId }, msg: '已提交审核' }
  827. }
  828. // 已上架商品禁止直接编辑(需先走下架审核)
  829. return { code: 400, data: null, msg: '当前状态不可提交审核' }
  830. })
  831. // 申请下架
  832. Mock.mock(/\/api\/equity\/request-offshelf/, 'post', (options) => {
  833. const body = JSON.parse(options.body)
  834. const idx = equityList.findIndex(e => e.productId === body.productId)
  835. if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
  836. const cur = equityList[idx]
  837. if (cur.shelfStatus !== '已上架' || cur.auditStatus !== '审核通过') {
  838. return { code: 400, data: null, msg: '仅已上架且无审核中的权益可申请下架' }
  839. }
  840. equityList[idx] = { ...cur, auditStatus: '下架待审核' }
  841. auditList.unshift({
  842. id: nextAuditId(),
  843. auditType: 'offshelf',
  844. bizModule: 'equity_product',
  845. bizModuleName: '借记卡权益',
  846. bizId: cur.productId,
  847. bizName: cur.equityName,
  848. snapshot: snapshotOf(cur),
  849. submitReason: '',
  850. submitterId: 'u001',
  851. submitterName: body.submitterName || '李运营',
  852. submitTime: nowStr(),
  853. auditorId: null,
  854. auditorName: '',
  855. auditTime: '',
  856. auditStatus: 'pending',
  857. auditComment: ''
  858. })
  859. return { code: 200, data: null, msg: '已提交下架审核' }
  860. })
  861. // 删除权益(仅草稿/已下架)
  862. Mock.mock(/\/api\/equity\/delete/, 'post', (options) => {
  863. const body = JSON.parse(options.body)
  864. const idx = equityList.findIndex(e => e.productId === body.productId)
  865. if (idx < 0) return { code: 404, data: null, msg: '权益不存在' }
  866. const cur = equityList[idx]
  867. const canDelete = (cur.shelfStatus === '未上架' && (cur.auditStatus === '草稿' || cur.auditStatus === '审核通过'))
  868. if (!canDelete) return { code: 400, data: null, msg: '当前状态不可删除' }
  869. equityList.splice(idx, 1)
  870. return { code: 200, data: null, msg: '删除成功' }
  871. })
  872. // 审核中心 - 列表
  873. Mock.mock(/\/api\/audit\/list/, 'get', (options) => {
  874. const url = new URL(options.url, 'http://localhost')
  875. const auditStatus = url.searchParams.get('auditStatus') || ''
  876. const bizName = url.searchParams.get('bizName') || ''
  877. const bizId = url.searchParams.get('bizId') || ''
  878. let filtered = auditList
  879. if (auditStatus) filtered = filtered.filter(a => a.auditStatus === auditStatus)
  880. if (bizName) filtered = filtered.filter(a => a.bizName.includes(bizName))
  881. if (bizId) filtered = filtered.filter(a => String(a.bizId).includes(bizId))
  882. return { code: 200, data: filtered, msg: 'ok' }
  883. })
  884. // 审核中心 - 详情
  885. Mock.mock(/\/api\/audit\/detail/, 'get', (options) => {
  886. const url = new URL(options.url, 'http://localhost')
  887. const id = url.searchParams.get('id')
  888. const item = auditList.find(a => a.id === id)
  889. return item
  890. ? { code: 200, data: item, msg: 'ok' }
  891. : { code: 404, data: null, msg: '审核单不存在' }
  892. })
  893. // 审核中心 - 通过
  894. Mock.mock(/\/api\/audit\/approve/, 'post', (options) => {
  895. const body = JSON.parse(options.body)
  896. const idx = auditList.findIndex(a => a.id === body.id)
  897. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  898. const audit = auditList[idx]
  899. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  900. const eqIdx = equityList.findIndex(e => e.productId === audit.bizId)
  901. if (eqIdx < 0) return { code: 404, data: null, msg: '关联权益不存在' }
  902. const eq = equityList[eqIdx]
  903. const now = nowStr()
  904. const auditor = body.auditorName || '王五'
  905. if (audit.auditType === 'new') {
  906. equityList[eqIdx] = { ...eq, shelfStatus: '已上架', auditStatus: '审核通过', reviewer: auditor, reviewTime: now }
  907. // 券码商品且关联了待导入批次:首次上架通过时模拟 SIP 导入、移入已导入列表
  908. if (audit.snapshot && audit.snapshot.couponBatchId) {
  909. importPendingBatch(audit.snapshot.couponBatchId, eq.productId, eq.equityName)
  910. }
  911. } else if (audit.auditType === 'offshelf') {
  912. equityList[eqIdx] = { ...eq, shelfStatus: '未上架', auditStatus: '审核通过', reviewer: auditor, reviewTime: now }
  913. }
  914. auditList[idx] = { ...audit, auditStatus: 'approved', auditorId: 'u099', auditorName: auditor, auditTime: now, auditComment: body.comment || '' }
  915. return { code: 200, data: null, msg: '审核通过' }
  916. })
  917. // 审核中心 - 驳回
  918. Mock.mock(/\/api\/audit\/reject/, 'post', (options) => {
  919. const body = JSON.parse(options.body)
  920. const idx = auditList.findIndex(a => a.id === body.id)
  921. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  922. const audit = auditList[idx]
  923. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  924. const eqIdx = equityList.findIndex(e => e.productId === audit.bizId)
  925. if (eqIdx < 0) return { code: 404, data: null, msg: '关联权益不存在' }
  926. const eq = equityList[eqIdx]
  927. const now = nowStr()
  928. const auditor = body.auditorName || '王五'
  929. // 驳回后权益主表回退到提审前的状态
  930. if (audit.auditType === 'new') {
  931. equityList[eqIdx] = { ...eq, shelfStatus: '未上架', auditStatus: '草稿' }
  932. } else if (audit.auditType === 'offshelf') {
  933. equityList[eqIdx] = { ...eq, shelfStatus: '已上架', auditStatus: '审核通过' }
  934. }
  935. auditList[idx] = { ...audit, auditStatus: 'rejected', auditorId: 'u099', auditorName: auditor, auditTime: now, auditComment: body.comment || '' }
  936. return { code: 200, data: null, msg: '已驳回' }
  937. })
  938. // =============================================
  939. // 发放计划
  940. // =============================================
  941. const equityGrantCampaignList = [
  942. {
  943. id: 'P000002',
  944. name: '夏日畅享权益月',
  945. startTime: '2026-05-01 00:00:00',
  946. endTime: '2026-08-31 23:59:59',
  947. targetType: '潜客',
  948. prospectTypes: ['无客户号潜客', '标签潜客'],
  949. crowdList: [],
  950. isAutoGrant: '是',
  951. grantMode: 'claim',
  952. equityList: ['Q100001', 'Q100004', 'Q100005'],
  953. grantCycle: 'total',
  954. grantQuantity: 1,
  955. validity: { type: 'relative', fixedRange: [], relativeDays: 30 },
  956. auditStatus: '审核通过',
  957. createTime: '2026-04-20 14:10:00',
  958. updateTime: '2026-04-20 14:10:00'
  959. },
  960. {
  961. id: 'P000001',
  962. name: '春季会员尊享礼遇',
  963. startTime: '2026-04-01 00:00:00',
  964. endTime: '2026-06-30 23:59:59',
  965. targetType: '客群',
  966. prospectTypes: [],
  967. crowdList: ['00000120251224'],
  968. isAutoGrant: '否',
  969. grantMode: 'direct',
  970. equityList: ['Q100001'],
  971. grantCycle: 'monthly',
  972. grantQuantity: 2,
  973. validity: { type: 'fixed', fixedRange: ['2026-04-01', '2026-06-30'], relativeDays: 30 },
  974. auditStatus: '新增待审核',
  975. createTime: '2026-03-25 10:20:30',
  976. updateTime: '2026-03-25 10:20:30'
  977. }
  978. ]
  979. let equityGrantCampaignCounter = equityGrantCampaignList.length
  980. function nextGrantCampaignId() {
  981. equityGrantCampaignCounter += 1
  982. return 'P' + String(equityGrantCampaignCounter).padStart(6, '0')
  983. }
  984. Mock.mock(/\/api\/equity-grant-campaign\/list/, 'get', (options) => {
  985. const url = new URL(options.url, 'http://localhost')
  986. const name = url.searchParams.get('name') || ''
  987. const startDate = url.searchParams.get('startDate') || ''
  988. const endDate = url.searchParams.get('endDate') || ''
  989. let filtered = equityGrantCampaignList
  990. if (name) filtered = filtered.filter(c => c.name.includes(name))
  991. if (startDate) filtered = filtered.filter(c => c.endTime.slice(0, 10) >= startDate)
  992. if (endDate) filtered = filtered.filter(c => c.startTime.slice(0, 10) <= endDate)
  993. return { code: 200, data: filtered, msg: 'ok' }
  994. })
  995. Mock.mock(/\/api\/equity-grant-campaign\/save/, 'post', (options) => {
  996. const body = JSON.parse(options.body)
  997. const now = nowStr()
  998. const defaultValidity = { type: 'fixed', fixedRange: [], relativeDays: 30 }
  999. if (body.id) {
  1000. const idx = equityGrantCampaignList.findIndex(c => c.id === body.id)
  1001. if (idx < 0) return { code: 404, data: null, msg: '发放计划不存在' }
  1002. equityGrantCampaignList[idx] = {
  1003. ...equityGrantCampaignList[idx],
  1004. name: body.name,
  1005. startTime: body.startTime,
  1006. endTime: body.endTime,
  1007. targetType: body.targetType || '客群',
  1008. prospectTypes: body.prospectTypes || [],
  1009. crowdList: body.crowdList || [],
  1010. isAutoGrant: body.isAutoGrant || '是',
  1011. equityList: body.equityList || [],
  1012. grantCycle: body.grantCycle || 'total',
  1013. grantQuantity: body.grantQuantity || 1,
  1014. validity: body.validity || defaultValidity,
  1015. updateTime: now
  1016. }
  1017. return { code: 200, data: { id: body.id }, msg: '保存成功' }
  1018. }
  1019. const newId = nextGrantCampaignId()
  1020. equityGrantCampaignList.unshift({
  1021. id: newId,
  1022. name: body.name,
  1023. startTime: body.startTime,
  1024. endTime: body.endTime,
  1025. targetType: body.targetType || '客群',
  1026. prospectTypes: body.prospectTypes || [],
  1027. crowdList: body.crowdList || [],
  1028. isAutoGrant: body.isAutoGrant || '是',
  1029. equityList: body.equityList || [],
  1030. grantCycle: body.grantCycle || 'total',
  1031. grantQuantity: body.grantQuantity || 1,
  1032. validity: body.validity || defaultValidity,
  1033. auditStatus: '新增待审核',
  1034. createTime: now,
  1035. updateTime: now
  1036. })
  1037. return { code: 200, data: { id: newId }, msg: '新建成功' }
  1038. })
  1039. Mock.mock(/\/api\/equity-grant-campaign\/delete/, 'post', (options) => {
  1040. const body = JSON.parse(options.body)
  1041. const idx = equityGrantCampaignList.findIndex(c => c.id === body.id)
  1042. if (idx < 0) return { code: 404, data: null, msg: '发放计划不存在' }
  1043. equityGrantCampaignList.splice(idx, 1)
  1044. return { code: 200, data: null, msg: '删除成功' }
  1045. })
  1046. // =============================================
  1047. // 发放任务
  1048. // =============================================
  1049. const grantTaskList = [
  1050. {
  1051. taskId: 'T20260501001',
  1052. planName: '夏日畅享权益月',
  1053. planId: 'P000002',
  1054. taskType: '自动',
  1055. taskCycle: '2026-05-01 ~ 2026-05-31',
  1056. status: '已完成',
  1057. createTime: '2026-05-01 00:00:00',
  1058. executeTime: '2026-05-01 00:05:12',
  1059. executor: '系统'
  1060. },
  1061. {
  1062. taskId: 'T20260401001',
  1063. planName: '春季会员尊享礼遇',
  1064. planId: 'P000001',
  1065. taskType: '手动',
  1066. taskCycle: '2026-04-01 ~ 2026-04-30',
  1067. status: '待执行',
  1068. createTime: '2026-04-01 00:00:00',
  1069. executeTime: '',
  1070. executor: ''
  1071. },
  1072. {
  1073. taskId: 'T20260401002',
  1074. planName: '春季会员尊享礼遇',
  1075. planId: 'P000001',
  1076. taskType: '手动',
  1077. taskCycle: '2026-05-01 ~ 2026-05-31',
  1078. status: '执行中',
  1079. createTime: '2026-05-01 00:00:00',
  1080. executeTime: '2026-05-01 10:30:00',
  1081. executor: '张三'
  1082. }
  1083. ]
  1084. Mock.mock(/\/api\/grant-task\/list/, 'get', (options) => {
  1085. const url = new URL(options.url, 'http://localhost')
  1086. const planName = url.searchParams.get('planName') || ''
  1087. const status = url.searchParams.get('status') || ''
  1088. let filtered = grantTaskList
  1089. if (planName) filtered = filtered.filter(t => t.planName.includes(planName))
  1090. if (status) filtered = filtered.filter(t => t.status === status)
  1091. return { code: 200, data: filtered, msg: 'ok' }
  1092. })
  1093. Mock.mock(/\/api\/grant-task\/detail/, 'get', (options) => {
  1094. const url = new URL(options.url, 'http://localhost')
  1095. const taskId = url.searchParams.get('taskId') || ''
  1096. const task = grantTaskList.find(t => t.taskId === taskId)
  1097. if (!task) return { code: 404, data: null, msg: '任务不存在' }
  1098. const plan = equityGrantCampaignList.find(c => c.id === task.planId)
  1099. const cycleMap = {
  1100. 'total': '整个周期',
  1101. 'monthly': '每月',
  1102. 'quarterly': '每季度',
  1103. 'yearly': '每年'
  1104. }
  1105. const planData = {
  1106. ...task,
  1107. grantMode: plan ? plan.grantMode : 'claim',
  1108. isAutoGrant: plan ? plan.isAutoGrant : '是',
  1109. planStartTime: plan ? plan.startTime : '',
  1110. planEndTime: plan ? plan.endTime : '',
  1111. grantQuantityLabel: plan ? `${cycleMap[plan.grantCycle] || plan.grantCycle} ${plan.grantQuantity} 个` : '',
  1112. validityLabel: plan ? (plan.validity.type === 'fixed' ? (plan.validity.fixedRange.join(' 至 ')) : `自发放起 ${plan.validity.relativeDays} 天内有效`) : '',
  1113. crowdLabel: plan ? (plan.crowdList.length ? plan.crowdList.map(id => {
  1114. const crowdNames = { '00000120251224': '层层礼圈选客群1' }
  1115. return crowdNames[id] ? `${crowdNames[id]} (${id})` : id
  1116. }).join('、') : '-') : '-',
  1117. equityLabel: plan ? (plan.equityList.length ? plan.equityList.map(id => {
  1118. const e = equityList.find(p => p.productId === id)
  1119. return e ? `${e.equityName} (${id})` : id
  1120. }).join('、') : '-') : '-',
  1121. smsLabel: plan ? '发放成功通用模板' : '-'
  1122. }
  1123. return { code: 200, data: planData, msg: 'ok' }
  1124. })
  1125. Mock.mock(/\/api\/grant-task\/execute/, 'post', (options) => {
  1126. const body = JSON.parse(options.body)
  1127. const idx = grantTaskList.findIndex(t => t.taskId === body.taskId)
  1128. if (idx < 0) return { code: 404, data: null, msg: '任务不存在' }
  1129. if (grantTaskList[idx].status !== '待执行') return { code: 400, data: null, msg: '该任务不在待执行状态' }
  1130. grantTaskList[idx].status = '执行中'
  1131. grantTaskList[idx].executeTime = nowStr()
  1132. grantTaskList[idx].executor = '运营人员'
  1133. return { code: 200, data: null, msg: '执行成功' }
  1134. })
  1135. Mock.mock(/\/api\/equity-grant-campaign\/detail/, 'get', (options) => {
  1136. const url = new URL(options.url, 'http://localhost')
  1137. const id = url.searchParams.get('id')
  1138. const item = equityGrantCampaignList.find(c => c.id === id)
  1139. return item
  1140. ? { code: 200, data: item, msg: 'ok' }
  1141. : { code: 404, data: null, msg: '发放计划不存在' }
  1142. })
  1143. Mock.mock(/\/api\/equity-grant-campaign\/end/, 'post', (options) => {
  1144. const body = JSON.parse(options.body)
  1145. const idx = equityGrantCampaignList.findIndex(c => c.id === body.id)
  1146. if (idx < 0) return { code: 404, data: null, msg: '发放计划不存在' }
  1147. const cur = equityGrantCampaignList[idx]
  1148. if (cur.auditStatus !== '审核通过') return { code: 400, data: null, msg: '仅审核通过状态可提交结束审核' }
  1149. // 创建审核单并更新发放计划状态为结束待审核
  1150. const auditId = nextCampaignAuditId()
  1151. const now = nowStr()
  1152. equityGrantCampaignList[idx] = { ...cur, auditStatus: '结束待审核', updateTime: now }
  1153. campaignAuditList.unshift({
  1154. id: auditId,
  1155. auditType: 'end',
  1156. bizModuleName: '发放计划',
  1157. bizId: cur.id,
  1158. bizName: cur.name,
  1159. snapshot: {
  1160. name: cur.name,
  1161. startTime: cur.startTime,
  1162. endTime: cur.endTime,
  1163. grantMode: cur.grantMode,
  1164. targetType: cur.targetType || '客群',
  1165. prospectTypes: cur.prospectTypes || [],
  1166. crowdList: cur.crowdList || [],
  1167. equityList: cur.equityList || [],
  1168. grantCycle: cur.grantCycle,
  1169. grantQuantity: cur.grantQuantity,
  1170. validity: cur.validity,
  1171. smsTemplateId: cur.smsTemplateId || '',
  1172. smsTemplate: cur.smsTemplate || ''
  1173. },
  1174. submitterName: body.submitterName || '运营',
  1175. submitTime: now,
  1176. auditorName: '',
  1177. auditTime: '',
  1178. auditStatus: 'pending',
  1179. auditComment: ''
  1180. })
  1181. return { code: 200, data: { auditId }, msg: '已提交结束审核' }
  1182. })
  1183. // =============================================
  1184. // 规则组审核
  1185. // =============================================
  1186. let ruleGroupAuditIdCounter = 200
  1187. function nextRuleGroupAuditId() {
  1188. ruleGroupAuditIdCounter += 1
  1189. const d = new Date()
  1190. const ymd = d.getFullYear().toString() + String(d.getMonth() + 1).padStart(2, '0') + String(d.getDate()).padStart(2, '0')
  1191. return 'RGA' + ymd + String(ruleGroupAuditIdCounter).padStart(4, '0')
  1192. }
  1193. const ruleGroupAuditList = [
  1194. {
  1195. id: 'RGA202605250001',
  1196. auditType: 'new',
  1197. bizModuleName: '规则组',
  1198. bizId: 'RG20260420003',
  1199. bizName: '借记卡-跨境候选客群',
  1200. submitterName: '赵市场',
  1201. submitTime: '2026-05-25 10:00:00',
  1202. auditorName: '',
  1203. auditTime: '',
  1204. auditStatus: 'pending',
  1205. auditComment: ''
  1206. },
  1207. {
  1208. id: 'RGA202605230001',
  1209. auditType: 'new',
  1210. bizModuleName: '规则组',
  1211. bizId: 'RG20260401001',
  1212. bizName: '层层礼-借记卡新客圈选',
  1213. submitterName: '张运营',
  1214. submitTime: '2026-05-23 14:30:00',
  1215. auditorName: '王审核',
  1216. auditTime: '2026-05-24 09:00:00',
  1217. auditStatus: 'approved',
  1218. auditComment: '规则配置合理,准予上线'
  1219. }
  1220. ]
  1221. Mock.mock(/\/api\/rule-group-audit\/list/, 'get', (options) => {
  1222. const url = new URL(options.url, 'http://localhost')
  1223. const auditStatus = url.searchParams.get('auditStatus') || ''
  1224. const bizName = url.searchParams.get('bizName') || ''
  1225. const bizId = url.searchParams.get('bizId') || ''
  1226. let filtered = ruleGroupAuditList
  1227. if (auditStatus) filtered = filtered.filter(a => a.auditStatus === auditStatus)
  1228. if (bizName) filtered = filtered.filter(a => a.bizName.includes(bizName))
  1229. if (bizId) filtered = filtered.filter(a => String(a.bizId).includes(bizId))
  1230. return { code: 200, data: filtered, msg: 'ok' }
  1231. })
  1232. Mock.mock(/\/api\/rule-group-audit\/detail/, 'get', (options) => {
  1233. const url = new URL(options.url, 'http://localhost')
  1234. const id = url.searchParams.get('id')
  1235. const item = ruleGroupAuditList.find(a => a.id === id)
  1236. return item
  1237. ? { code: 200, data: item, msg: 'ok' }
  1238. : { code: 404, data: null, msg: '审核单不存在' }
  1239. })
  1240. Mock.mock(/\/api\/rule-group-audit\/approve/, 'post', (options) => {
  1241. const body = JSON.parse(options.body)
  1242. const idx = ruleGroupAuditList.findIndex(a => a.id === body.id)
  1243. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  1244. const audit = ruleGroupAuditList[idx]
  1245. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  1246. const now = nowStr()
  1247. ruleGroupAuditList[idx] = { ...audit, auditStatus: 'approved', auditorName: body.auditorName || '王审核', auditTime: now, auditComment: body.comment || '' }
  1248. return { code: 200, data: null, msg: '审核通过' }
  1249. })
  1250. Mock.mock(/\/api\/rule-group-audit\/reject/, 'post', (options) => {
  1251. const body = JSON.parse(options.body)
  1252. const idx = ruleGroupAuditList.findIndex(a => a.id === body.id)
  1253. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  1254. const audit = ruleGroupAuditList[idx]
  1255. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  1256. const now = nowStr()
  1257. ruleGroupAuditList[idx] = { ...audit, auditStatus: 'rejected', auditorName: body.auditorName || '王审核', auditTime: now, auditComment: body.comment || '' }
  1258. return { code: 200, data: null, msg: '已驳回' }
  1259. })
  1260. // =============================================
  1261. // 发放计划审核
  1262. // =============================================
  1263. let campaignAuditIdCounter = 100
  1264. function nextCampaignAuditId() {
  1265. campaignAuditIdCounter += 1
  1266. const d = new Date()
  1267. const ymd = d.getFullYear().toString() + String(d.getMonth() + 1).padStart(2, '0') + String(d.getDate()).padStart(2, '0')
  1268. return 'CA' + ymd + String(campaignAuditIdCounter).padStart(4, '0')
  1269. }
  1270. const campaignAuditList = [
  1271. {
  1272. id: 'CA202605200001',
  1273. auditType: 'new',
  1274. bizModuleName: '发放计划',
  1275. bizId: 'P000003',
  1276. bizName: '秋季养生权益季',
  1277. snapshot: {
  1278. name: '秋季养生权益季',
  1279. startTime: '2026-09-01 00:00:00',
  1280. endTime: '2026-11-30 23:59:59',
  1281. grantMode: 'claim',
  1282. crowdList: ['00000120251224'],
  1283. equityList: ['Q100001', 'Q100004'],
  1284. grantCycle: 'monthly',
  1285. grantQuantity: 1,
  1286. validity: { type: 'relative', fixedRange: [], relativeDays: 30 },
  1287. smsTemplateId: 'GRANT_SMS_001',
  1288. smsTemplate: '【银行】尊敬的${customerName},您已获得${productName}权益,发放时间:${grantTime},请于${expireDate}前使用。'
  1289. },
  1290. submitterName: '王运营',
  1291. submitTime: '2026-05-20 09:30:00',
  1292. auditorName: '',
  1293. auditTime: '',
  1294. auditStatus: 'pending',
  1295. auditComment: ''
  1296. },
  1297. {
  1298. id: 'CA202605220001',
  1299. auditType: 'new',
  1300. bizModuleName: '发放计划',
  1301. bizId: 'P000004',
  1302. bizName: '冬季暖心权益发放',
  1303. snapshot: {
  1304. name: '冬季暖心权益发放',
  1305. startTime: '2026-12-01 00:00:00',
  1306. endTime: '2027-02-28 23:59:59',
  1307. grantMode: 'direct',
  1308. crowdList: ['00000120251224'],
  1309. equityList: ['Q100001'],
  1310. grantCycle: 'total',
  1311. grantQuantity: 3,
  1312. validity: { type: 'fixed', fixedRange: ['2026-12-01', '2027-02-28'], relativeDays: 30 },
  1313. smsTemplateId: 'GRANT_SMS_002',
  1314. smsTemplate: '【银行】尊敬的${customerName},您已获得${productName}权益,有效期至${expireDate},请登录APP查看详情。'
  1315. },
  1316. submitterName: '陈运营',
  1317. submitTime: '2026-05-22 10:15:00',
  1318. auditorName: '',
  1319. auditTime: '',
  1320. auditStatus: 'pending',
  1321. auditComment: ''
  1322. },
  1323. {
  1324. id: 'CA202605180001',
  1325. auditType: 'new',
  1326. bizModuleName: '发放计划',
  1327. bizId: 'P000001',
  1328. bizName: '春季会员尊享礼遇',
  1329. snapshot: {
  1330. name: '春季会员尊享礼遇',
  1331. startTime: '2026-04-01 00:00:00',
  1332. endTime: '2026-06-30 23:59:59',
  1333. grantMode: 'direct',
  1334. crowdList: ['00000120251224'],
  1335. equityList: ['Q100001'],
  1336. grantCycle: 'monthly',
  1337. grantQuantity: 2,
  1338. validity: { type: 'fixed', fixedRange: ['2026-04-01', '2026-06-30'], relativeDays: 30 },
  1339. smsTemplateId: 'GRANT_SMS_003',
  1340. smsTemplate: '【银行】${customerName}您好,${productName}已为您发放成功,有效期至${expireDate}。'
  1341. },
  1342. submitterName: '李运营',
  1343. submitTime: '2026-05-18 14:20:00',
  1344. auditorName: '王五',
  1345. auditTime: '2026-05-19 09:00:00',
  1346. auditStatus: 'approved',
  1347. auditComment: '发放计划内容合规,准予通过'
  1348. }
  1349. ]
  1350. Mock.mock(/\/api\/campaign-audit\/list/, 'get', (options) => {
  1351. const url = new URL(options.url, 'http://localhost')
  1352. const auditStatus = url.searchParams.get('auditStatus') || ''
  1353. const bizId = url.searchParams.get('bizId') || ''
  1354. let filtered = campaignAuditList
  1355. if (auditStatus) filtered = filtered.filter(a => a.auditStatus === auditStatus)
  1356. if (bizId) filtered = filtered.filter(a => a.bizId === bizId)
  1357. return { code: 200, data: filtered, msg: 'ok' }
  1358. })
  1359. Mock.mock(/\/api\/campaign-audit\/detail/, 'get', (options) => {
  1360. const url = new URL(options.url, 'http://localhost')
  1361. const id = url.searchParams.get('id')
  1362. const item = campaignAuditList.find(a => a.id === id)
  1363. return item
  1364. ? { code: 200, data: item, msg: 'ok' }
  1365. : { code: 404, data: null, msg: '审核单不存在' }
  1366. })
  1367. Mock.mock(/\/api\/campaign-audit\/approve/, 'post', (options) => {
  1368. const body = JSON.parse(options.body)
  1369. const idx = campaignAuditList.findIndex(a => a.id === body.id)
  1370. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  1371. const audit = campaignAuditList[idx]
  1372. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  1373. const now = nowStr()
  1374. campaignAuditList[idx] = { ...audit, auditStatus: 'approved', auditorName: body.auditorName || '王五', auditTime: now, auditComment: body.comment || '' }
  1375. // 同步更新发放计划主表
  1376. const campaignIdx = equityGrantCampaignList.findIndex(c => c.id === audit.bizId)
  1377. if (campaignIdx >= 0) {
  1378. if (audit.auditType === 'new') {
  1379. equityGrantCampaignList[campaignIdx].auditStatus = '审核通过'
  1380. } else if (audit.auditType === 'end') {
  1381. // 结束审核通过:实际结束发放计划
  1382. equityGrantCampaignList[campaignIdx] = {
  1383. ...equityGrantCampaignList[campaignIdx],
  1384. endTime: now,
  1385. auditStatus: '审核通过',
  1386. updateTime: now
  1387. }
  1388. }
  1389. }
  1390. return { code: 200, data: null, msg: '审核通过' }
  1391. })
  1392. Mock.mock(/\/api\/campaign-audit\/reject/, 'post', (options) => {
  1393. const body = JSON.parse(options.body)
  1394. const idx = campaignAuditList.findIndex(a => a.id === body.id)
  1395. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  1396. const audit = campaignAuditList[idx]
  1397. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  1398. const now = nowStr()
  1399. campaignAuditList[idx] = { ...audit, auditStatus: 'rejected', auditorName: body.auditorName || '王五', auditTime: now, auditComment: body.comment || '' }
  1400. // 驳回后回退发放计划审核状态为审核通过,允许重新编辑/结束
  1401. const campaignIdx = equityGrantCampaignList.findIndex(c => c.id === audit.bizId)
  1402. if (campaignIdx >= 0) equityGrantCampaignList[campaignIdx] = {
  1403. ...equityGrantCampaignList[campaignIdx],
  1404. auditStatus: '审核通过',
  1405. updateTime: now
  1406. }
  1407. return { code: 200, data: null, msg: '已驳回' }
  1408. })
  1409. // ==================== 券码审核 ====================
  1410. const couponAuditList = [
  1411. {
  1412. id: 'CPA202606040001',
  1413. type: 'import',
  1414. productName: '权益A',
  1415. productId: 'Q100001',
  1416. importCount: 1000,
  1417. effectiveTime: '2026-06-05 00:00:00',
  1418. expireTime: '2026-12-31 23:59:59',
  1419. creator: '张三',
  1420. createTime: '2026-06-04 10:32:18',
  1421. auditorName: '',
  1422. auditTime: '',
  1423. auditStatus: 'pending'
  1424. },
  1425. {
  1426. id: 'CPA202606030001',
  1427. type: 'import',
  1428. productName: '微信立减金 5元(满10减5)',
  1429. productId: 'Q100007',
  1430. importCount: 5000,
  1431. effectiveTime: '2026-06-10 00:00:00',
  1432. expireTime: '2026-09-30 23:59:59',
  1433. creator: '李四',
  1434. createTime: '2026-06-03 15:08:42',
  1435. auditorName: '',
  1436. auditTime: '',
  1437. auditStatus: 'pending'
  1438. },
  1439. {
  1440. id: 'CPA202605280001',
  1441. type: 'import',
  1442. productName: '权益B',
  1443. productId: 'Q100002',
  1444. importCount: 500,
  1445. effectiveTime: '2026-06-01 00:00:00',
  1446. expireTime: '2026-12-31 23:59:59',
  1447. creator: '李四',
  1448. createTime: '2026-05-28 14:15:42',
  1449. auditorName: '王五',
  1450. auditTime: '2026-05-29 09:20:00',
  1451. auditStatus: 'approved'
  1452. },
  1453. {
  1454. id: 'CPA202605200001',
  1455. type: 'import',
  1456. productName: '权益C',
  1457. productId: 'Q100003',
  1458. importCount: 800,
  1459. effectiveTime: '2026-05-25 00:00:00',
  1460. expireTime: '2026-10-31 23:59:59',
  1461. creator: '王五',
  1462. createTime: '2026-05-20 09:08:11',
  1463. auditorName: '赵管理',
  1464. auditTime: '2026-05-21 16:30:00',
  1465. auditStatus: 'approved'
  1466. },
  1467. {
  1468. id: 'CPA202605150001',
  1469. type: 'import',
  1470. productName: '权益D',
  1471. productId: 'Q100004',
  1472. importCount: 200,
  1473. effectiveTime: '2026-05-20 00:00:00',
  1474. expireTime: '2026-08-31 23:59:59',
  1475. creator: '赵六',
  1476. createTime: '2026-05-15 16:45:30',
  1477. auditorName: '王五',
  1478. auditTime: '2026-05-16 10:05:00',
  1479. auditStatus: 'rejected'
  1480. },
  1481. {
  1482. id: 'CPA202606040002',
  1483. type: 'recycle_unissued',
  1484. productName: '权益B',
  1485. productId: 'Q100002',
  1486. importCount: 100,
  1487. effectiveTime: '2026-06-01 00:00:00',
  1488. expireTime: '2026-12-31 23:59:59',
  1489. creator: '张三',
  1490. createTime: '2026-06-04 16:40:00',
  1491. auditorName: '',
  1492. auditTime: '',
  1493. auditStatus: 'pending'
  1494. },
  1495. {
  1496. id: 'CPA202606020001',
  1497. type: 'recycle_expired',
  1498. productName: '权益C',
  1499. productId: 'Q100003',
  1500. importCount: 50,
  1501. effectiveTime: '2026-05-25 00:00:00',
  1502. expireTime: '2026-06-01 23:59:59',
  1503. creator: '王五',
  1504. createTime: '2026-06-02 11:20:00',
  1505. auditorName: '',
  1506. auditTime: '',
  1507. auditStatus: 'pending'
  1508. },
  1509. {
  1510. id: 'CPA202606010001',
  1511. type: 'import',
  1512. productName: '权益C',
  1513. productId: 'Q100003',
  1514. importCount: 1500,
  1515. effectiveTime: '2026-06-08 00:00:00',
  1516. expireTime: '2026-12-31 23:59:59',
  1517. creator: '王五',
  1518. createTime: '2026-06-01 09:15:00',
  1519. auditorName: '',
  1520. auditTime: '',
  1521. auditStatus: 'pending'
  1522. },
  1523. {
  1524. id: 'CPA202605310001',
  1525. type: 'recycle_wrong',
  1526. productName: '权益A',
  1527. productId: 'Q100001',
  1528. importCount: 20,
  1529. effectiveTime: '2026-04-16 00:00:00',
  1530. expireTime: '2026-12-31 23:59:59',
  1531. creator: '李四',
  1532. createTime: '2026-05-31 14:22:00',
  1533. auditorName: '',
  1534. auditTime: '',
  1535. auditStatus: 'pending'
  1536. },
  1537. {
  1538. id: 'CPA202605300001',
  1539. type: 'import',
  1540. productName: '权益D',
  1541. productId: 'Q100004',
  1542. importCount: 800,
  1543. effectiveTime: '2026-06-15 00:00:00',
  1544. expireTime: '2026-11-30 23:59:59',
  1545. creator: '赵六',
  1546. createTime: '2026-05-30 10:05:00',
  1547. auditorName: '',
  1548. auditTime: '',
  1549. auditStatus: 'pending'
  1550. },
  1551. {
  1552. id: 'CPA202605290001',
  1553. type: 'recycle_unissued',
  1554. productName: '权益A',
  1555. productId: 'Q100001',
  1556. importCount: 60,
  1557. effectiveTime: '2026-04-16 00:00:00',
  1558. expireTime: '2026-12-31 23:59:59',
  1559. creator: '张三',
  1560. createTime: '2026-05-29 16:48:00',
  1561. auditorName: '',
  1562. auditTime: '',
  1563. auditStatus: 'pending'
  1564. },
  1565. {
  1566. id: 'CPA202605270001',
  1567. type: 'import',
  1568. productName: '微信立减金 5元(满10减5)',
  1569. productId: 'Q100007',
  1570. importCount: 3000,
  1571. effectiveTime: '2026-06-01 00:00:00',
  1572. expireTime: '2026-09-30 23:59:59',
  1573. creator: '李四',
  1574. createTime: '2026-05-27 11:30:00',
  1575. auditorName: '',
  1576. auditTime: '',
  1577. auditStatus: 'pending'
  1578. },
  1579. {
  1580. id: 'CPA202605260001',
  1581. type: 'recycle_expired',
  1582. productName: '权益B',
  1583. productId: 'Q100002',
  1584. importCount: 35,
  1585. effectiveTime: '2026-04-21 00:00:00',
  1586. expireTime: '2026-05-20 23:59:59',
  1587. creator: '王五',
  1588. createTime: '2026-05-26 09:55:00',
  1589. auditorName: '',
  1590. auditTime: '',
  1591. auditStatus: 'pending'
  1592. },
  1593. {
  1594. id: 'CPA202605250001',
  1595. type: 'import',
  1596. productName: '权益E',
  1597. productId: 'Q100005',
  1598. importCount: 1200,
  1599. effectiveTime: '2026-06-01 00:00:00',
  1600. expireTime: '2026-10-31 23:59:59',
  1601. creator: '张三',
  1602. createTime: '2026-05-25 15:12:00',
  1603. auditorName: '',
  1604. auditTime: '',
  1605. auditStatus: 'pending'
  1606. },
  1607. {
  1608. id: 'CPA202605240001',
  1609. type: 'recycle_unissued',
  1610. productName: '权益C',
  1611. productId: 'Q100003',
  1612. importCount: 90,
  1613. effectiveTime: '2026-05-25 00:00:00',
  1614. expireTime: '2026-10-31 23:59:59',
  1615. creator: '赵六',
  1616. createTime: '2026-05-24 10:40:00',
  1617. auditorName: '',
  1618. auditTime: '',
  1619. auditStatus: 'pending'
  1620. },
  1621. {
  1622. id: 'CPA202605220001',
  1623. type: 'recycle_wrong',
  1624. productName: '权益D',
  1625. productId: 'Q100004',
  1626. importCount: 200,
  1627. effectiveTime: '2026-05-20 00:00:00',
  1628. expireTime: '2026-08-31 23:59:59',
  1629. creator: '赵六',
  1630. createTime: '2026-05-22 10:10:00',
  1631. auditorName: '王五',
  1632. auditTime: '2026-05-23 09:30:00',
  1633. auditStatus: 'approved'
  1634. }
  1635. ]
  1636. Mock.mock(/\/api\/coupon-audit\/list/, 'get', (options) => {
  1637. const url = new URL(options.url, 'http://localhost')
  1638. const auditStatus = url.searchParams.get('auditStatus') || ''
  1639. const productId = url.searchParams.get('productId') || ''
  1640. // 券码审核类型暂时仅保留「导入」,回收相关类型先去掉
  1641. let filtered = couponAuditList.filter(a => a.type === 'import')
  1642. if (auditStatus === 'reviewed') {
  1643. // 已审核 = 审核通过 + 审核驳回
  1644. filtered = filtered.filter(a => a.auditStatus === 'approved' || a.auditStatus === 'rejected')
  1645. } else if (auditStatus) {
  1646. filtered = filtered.filter(a => a.auditStatus === auditStatus)
  1647. }
  1648. if (productId) filtered = filtered.filter(a => a.productId === productId)
  1649. return { code: 200, data: filtered, msg: 'ok' }
  1650. })
  1651. Mock.mock(/\/api\/coupon-audit\/approve/, 'post', (options) => {
  1652. const body = JSON.parse(options.body)
  1653. const idx = couponAuditList.findIndex(a => a.id === body.id)
  1654. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  1655. const audit = couponAuditList[idx]
  1656. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  1657. couponAuditList[idx] = { ...audit, auditStatus: 'approved', auditorName: body.auditorName || '王五', auditTime: nowStr() }
  1658. // 联动待导入记录:已关联权益商品 → 模拟 SIP 导入、移入已导入列表;未关联 → 仅置为审核通过,留在待导入列表
  1659. if (audit.batchId) {
  1660. const pending = pendingCouponList.find(p => p.batchId === audit.batchId)
  1661. if (pending) {
  1662. pending.auditStatus = 'approved'
  1663. if (pending.productId) importPendingBatch(pending.batchId)
  1664. }
  1665. }
  1666. return { code: 200, data: null, msg: '审核通过' }
  1667. })
  1668. Mock.mock(/\/api\/coupon-audit\/reject/, 'post', (options) => {
  1669. const body = JSON.parse(options.body)
  1670. const idx = couponAuditList.findIndex(a => a.id === body.id)
  1671. if (idx < 0) return { code: 404, data: null, msg: '审核单不存在' }
  1672. const audit = couponAuditList[idx]
  1673. if (audit.auditStatus !== 'pending') return { code: 400, data: null, msg: '该审核单已完成' }
  1674. couponAuditList[idx] = { ...audit, auditStatus: 'rejected', auditorName: body.auditorName || '王五', auditTime: nowStr() }
  1675. if (audit.batchId) {
  1676. const pending = pendingCouponList.find(p => p.batchId === audit.batchId)
  1677. if (pending) pending.auditStatus = 'rejected'
  1678. }
  1679. return { code: 200, data: null, msg: '已驳回' }
  1680. })
  1681. // ==================== 已导入券码批次(全部借记卡权益商品) ====================
  1682. const couponBatchList = [
  1683. {
  1684. batchId: 'B100001', productId: 'Q100001', equityName: '权益A',
  1685. importCount: 1000, actualImportCount: 998, failCount: 2, remainingCount: 720,
  1686. effectiveTime: '2026-04-16 00:00:00', expireTime: '2026-12-31 23:59:59',
  1687. fileStatus: '处理成功', importTime: '2026-04-15 10:32:18', creator: '张三',
  1688. recycleRecords: [
  1689. { recycleTime: '2026-05-10 14:22:08', type: '未发放券码回收', operator: '张三', amount: 120 },
  1690. { recycleTime: '2026-04-28 09:15:33', type: '已过期券码回收', operator: '李四', amount: 80 }
  1691. ]
  1692. },
  1693. {
  1694. batchId: 'B100002', productId: 'Q100001', equityName: '权益A',
  1695. importCount: 500, actualImportCount: 500, failCount: 0, remainingCount: 320,
  1696. effectiveTime: '2026-04-21 00:00:00', expireTime: '2026-09-30 23:59:59',
  1697. fileStatus: '处理成功', importTime: '2026-04-20 14:15:42', creator: '李四',
  1698. recycleRecords: [
  1699. { recycleTime: '2026-04-12 16:40:51', type: '未发放券码回收', operator: '王五', amount: 200 }
  1700. ]
  1701. },
  1702. {
  1703. batchId: 'B100003', productId: 'Q100002', equityName: '权益B',
  1704. importCount: 800, actualImportCount: 0, failCount: 0, remainingCount: 0,
  1705. effectiveTime: '2026-05-08 00:00:00', expireTime: '2026-12-31 23:59:59',
  1706. fileStatus: '处理中', importTime: '2026-05-07 09:08:11', creator: '王五',
  1707. recycleRecords: []
  1708. },
  1709. {
  1710. batchId: 'B100004', productId: 'Q100002', equityName: '权益B',
  1711. importCount: 200, actualImportCount: 0, failCount: 200, remainingCount: 0,
  1712. effectiveTime: '2026-05-02 00:00:00', expireTime: '2026-10-31 23:59:59',
  1713. fileStatus: '处理失败', importTime: '2026-05-01 16:45:30', creator: '赵六',
  1714. recycleRecords: []
  1715. },
  1716. {
  1717. batchId: 'B100005', productId: 'Q100003', equityName: '权益C',
  1718. importCount: 1500, actualImportCount: 1500, failCount: 0, remainingCount: 1280,
  1719. effectiveTime: '2026-04-10 00:00:00', expireTime: '2026-12-31 23:59:59',
  1720. fileStatus: '处理成功', importTime: '2026-04-09 09:21:05', creator: '张三',
  1721. recycleRecords: []
  1722. },
  1723. {
  1724. batchId: 'B100006', productId: 'Q100003', equityName: '权益C',
  1725. importCount: 600, actualImportCount: 598, failCount: 2, remainingCount: 420,
  1726. effectiveTime: '2026-04-05 00:00:00', expireTime: '2026-11-30 23:59:59',
  1727. fileStatus: '处理成功', importTime: '2026-04-04 14:02:33', creator: '李四',
  1728. recycleRecords: []
  1729. },
  1730. {
  1731. batchId: 'B100007', productId: 'Q100004', equityName: '权益D',
  1732. importCount: 300, actualImportCount: 300, failCount: 0, remainingCount: 0,
  1733. effectiveTime: '2026-03-20 00:00:00', expireTime: '2026-09-30 23:59:59',
  1734. fileStatus: '处理成功', importTime: '2026-03-19 10:45:12', creator: '王五',
  1735. recycleRecords: []
  1736. },
  1737. {
  1738. batchId: 'B100008', productId: 'Q100004', equityName: '权益D',
  1739. importCount: 2000, actualImportCount: 1995, failCount: 5, remainingCount: 1620,
  1740. effectiveTime: '2026-03-15 00:00:00', expireTime: '2026-12-31 23:59:59',
  1741. fileStatus: '处理成功', importTime: '2026-03-14 16:30:48', creator: '张三',
  1742. recycleRecords: []
  1743. },
  1744. {
  1745. batchId: 'B100009', productId: 'Q100005', equityName: '权益E',
  1746. importCount: 450, actualImportCount: 450, failCount: 0, remainingCount: 130,
  1747. effectiveTime: '2026-03-01 00:00:00', expireTime: '2026-08-31 23:59:59',
  1748. fileStatus: '处理成功', importTime: '2026-02-28 11:18:27', creator: '李四',
  1749. recycleRecords: []
  1750. },
  1751. {
  1752. batchId: 'B100010', productId: 'Q100005', equityName: '权益E',
  1753. importCount: 800, actualImportCount: 796, failCount: 4, remainingCount: 510,
  1754. effectiveTime: '2026-02-20 00:00:00', expireTime: '2026-10-31 23:59:59',
  1755. fileStatus: '处理成功', importTime: '2026-02-19 15:55:09', creator: '王五',
  1756. recycleRecords: []
  1757. },
  1758. {
  1759. batchId: 'B100011', productId: 'Q100007', equityName: '微信立减金 5元(满10减5)',
  1760. importCount: 1200, actualImportCount: 1200, failCount: 0, remainingCount: 60,
  1761. effectiveTime: '2026-02-10 00:00:00', expireTime: '2026-07-31 23:59:59',
  1762. fileStatus: '处理成功', importTime: '2026-02-09 09:40:51', creator: '赵六',
  1763. recycleRecords: []
  1764. },
  1765. {
  1766. batchId: 'B100012', productId: 'Q100001', equityName: '权益A',
  1767. importCount: 350, actualImportCount: 348, failCount: 2, remainingCount: 0,
  1768. effectiveTime: '2026-01-15 00:00:00', expireTime: '2026-06-30 23:59:59',
  1769. fileStatus: '处理成功', importTime: '2026-01-14 13:27:36', creator: '张三',
  1770. recycleRecords: []
  1771. }
  1772. ]
  1773. // 已导入券码列表:权益编号、批次ID 均为精准匹配
  1774. Mock.mock(/\/api\/coupon\/batch-list/, 'get', (options) => {
  1775. const url = new URL(options.url, 'http://localhost')
  1776. const productId = url.searchParams.get('productId') || ''
  1777. const batchId = url.searchParams.get('batchId') || ''
  1778. let filtered = couponBatchList
  1779. if (productId) filtered = filtered.filter(b => b.productId === productId)
  1780. if (batchId) filtered = filtered.filter(b => b.batchId === batchId)
  1781. return { code: 200, data: filtered, msg: 'ok' }
  1782. })
  1783. // 导入券码:生成「待导入券码记录」+「券码审核单」,券码审核通过后才真正导入
  1784. Mock.mock(/\/api\/coupon\/import/, 'post', (options) => {
  1785. const body = JSON.parse(options.body)
  1786. let productId = ''
  1787. let equityName = ''
  1788. if (body.productId) {
  1789. const equity = equityList.find(e => e.productId === body.productId)
  1790. if (!equity) return { code: 404, data: null, msg: '权益商品不存在' }
  1791. productId = equity.productId
  1792. equityName = equity.equityName
  1793. }
  1794. const batchId = nextPendingBatchId()
  1795. // 原型读不了上传文件,导入数量用 mock 随机数模拟
  1796. const importCount = 100 + Math.floor(Math.random() * 4900)
  1797. const createTime = nowStr()
  1798. const pending = { batchId, productId, equityName, importCount, creator: '当前用户', createTime, auditStatus: 'pending' }
  1799. pendingCouponList.unshift(pending)
  1800. couponAuditList.unshift(makeImportAudit(pending))
  1801. return { code: 200, data: { batchId }, msg: '已提交,等待券码审核' }
  1802. })
  1803. Mock.mock(/\/api\/coupon\/recycle/, 'post', (options) => {
  1804. const body = JSON.parse(options.body)
  1805. const batch = couponBatchList.find(b => b.batchId === body.batchId)
  1806. if (!batch) return { code: 404, data: null, msg: '批次不存在' }
  1807. const amount = Number(body.amount) || 0
  1808. if (amount < 1) return { code: 400, data: null, msg: '回收数量无效' }
  1809. // 未发放券码回收会扣减剩余数量;已过期券码回收仅记录,不扣减剩余库存
  1810. if (body.type === 'unissued') {
  1811. batch.remainingCount = Math.max(0, Number(batch.remainingCount) - amount)
  1812. }
  1813. const typeLabel = body.type === 'expired' ? '已过期券码回收' : '未发放券码回收'
  1814. if (!batch.recycleRecords) batch.recycleRecords = []
  1815. batch.recycleRecords.unshift({ recycleTime: nowStr(), type: typeLabel, operator: '当前用户', amount })
  1816. return { code: 200, data: null, msg: '回收成功' }
  1817. })
  1818. // ==================== 待导入券码记录(券码先导入、后关联权益) ====================
  1819. function nextPendingBatchId() {
  1820. const d = new Date()
  1821. const p = n => String(n).padStart(2, '0')
  1822. return 'I' + d.getFullYear() + p(d.getMonth() + 1) + p(d.getDate()) + p(d.getHours()) + p(d.getMinutes()) + p(d.getSeconds())
  1823. }
  1824. const pendingCouponList = [
  1825. { batchId: 'I20260620090301', productId: '', equityName: '', importCount: 1000, creator: '张三', createTime: '2026-06-20 09:03:01', auditStatus: 'pending' },
  1826. { batchId: 'I20260620143512', productId: 'Q100001', equityName: '权益A', importCount: 500, creator: '李四', createTime: '2026-06-20 14:35:12', auditStatus: 'pending' },
  1827. { batchId: 'I20260618101055', productId: '', equityName: '', importCount: 1200, creator: '张三', createTime: '2026-06-18 10:10:55', auditStatus: 'approved' },
  1828. { batchId: 'I20260617161820', productId: '', equityName: '', importCount: 800, creator: '王五', createTime: '2026-06-17 16:18:20', auditStatus: 'approved' },
  1829. { batchId: 'I20260616113045', productId: 'Q100003', equityName: '权益C', importCount: 300, creator: '赵六', createTime: '2026-06-16 11:30:45', auditStatus: 'rejected' },
  1830. { batchId: 'I20260615153000', productId: '', equityName: '', importCount: 600, creator: '李四', createTime: '2026-06-15 15:30:00', auditStatus: 'rejected' }
  1831. ]
  1832. // 待导入记录 → 已导入批次(模拟 SIP 导入成功)
  1833. function importPendingBatch(batchId, productId, equityName) {
  1834. const idx = pendingCouponList.findIndex(p => p.batchId === batchId)
  1835. if (idx < 0) return false
  1836. const p = pendingCouponList[idx]
  1837. pendingCouponList.splice(idx, 1)
  1838. couponBatchList.unshift({
  1839. batchId: p.batchId,
  1840. productId: productId || p.productId,
  1841. equityName: equityName || p.equityName,
  1842. importCount: p.importCount,
  1843. actualImportCount: p.importCount,
  1844. failCount: 0,
  1845. remainingCount: p.importCount,
  1846. effectiveTime: '',
  1847. expireTime: '',
  1848. fileStatus: '处理成功',
  1849. importTime: nowStr(),
  1850. creator: p.creator,
  1851. recycleRecords: []
  1852. })
  1853. return true
  1854. }
  1855. // 由待导入记录派生对应的券码审核单
  1856. let couponAuditSeq = 0
  1857. function makeImportAudit(p) {
  1858. couponAuditSeq += 1
  1859. const reviewed = p.auditStatus !== 'pending'
  1860. return {
  1861. id: 'CPA' + p.batchId.slice(1) + String(couponAuditSeq).padStart(2, '0'),
  1862. type: 'import',
  1863. batchId: p.batchId,
  1864. productName: p.equityName || '',
  1865. productId: p.productId || '',
  1866. importCount: p.importCount,
  1867. effectiveTime: '',
  1868. expireTime: '',
  1869. creator: p.creator,
  1870. createTime: p.createTime,
  1871. auditorName: reviewed ? '王五' : '',
  1872. auditTime: reviewed ? p.createTime : '',
  1873. auditStatus: p.auditStatus
  1874. }
  1875. }
  1876. // 用待导入记录派生的审核单替换旧的占位 import 审核单(recycle 记录保留)
  1877. for (let i = couponAuditList.length - 1; i >= 0; i--) {
  1878. if (couponAuditList[i].type === 'import') couponAuditList.splice(i, 1)
  1879. }
  1880. pendingCouponList.forEach(p => couponAuditList.push(makeImportAudit(p)))
  1881. // 待导入券码列表(券码管理-待导入 Tab;选择批次弹窗传 status=approved&unlinked=1)
  1882. Mock.mock(/\/api\/coupon\/pending-list/, 'get', (options) => {
  1883. const url = new URL(options.url, 'http://localhost')
  1884. const batchId = url.searchParams.get('batchId') || ''
  1885. const status = url.searchParams.get('status') || ''
  1886. const unlinked = url.searchParams.get('unlinked') || ''
  1887. let filtered = pendingCouponList
  1888. if (batchId) filtered = filtered.filter(p => p.batchId === batchId)
  1889. if (status) filtered = filtered.filter(p => p.auditStatus === status)
  1890. if (unlinked === '1') filtered = filtered.filter(p => !p.productId)
  1891. return { code: 200, data: filtered, msg: 'ok' }
  1892. })
  1893. // 编辑待导入记录(仅审核拒绝可编辑:可改关联权益商品,提交后重新进入待审核)
  1894. Mock.mock(/\/api\/coupon\/pending-edit/, 'post', (options) => {
  1895. const body = JSON.parse(options.body)
  1896. const p = pendingCouponList.find(x => x.batchId === body.batchId)
  1897. if (!p) return { code: 404, data: null, msg: '记录不存在' }
  1898. if (p.auditStatus !== 'rejected') return { code: 400, data: null, msg: '仅审核拒绝的记录可编辑' }
  1899. p.auditStatus = 'pending'
  1900. const audit = couponAuditList.find(a => a.type === 'import' && a.batchId === p.batchId)
  1901. if (audit) {
  1902. audit.auditStatus = 'pending'
  1903. audit.auditorName = ''
  1904. audit.auditTime = ''
  1905. }
  1906. return { code: 200, data: null, msg: '已重新提交审核' }
  1907. })
  1908. // 删除待导入记录(仅审核拒绝可删除)
  1909. Mock.mock(/\/api\/coupon\/pending-delete/, 'post', (options) => {
  1910. const body = JSON.parse(options.body)
  1911. const idx = pendingCouponList.findIndex(p => p.batchId === body.batchId)
  1912. if (idx < 0) return { code: 404, data: null, msg: '记录不存在' }
  1913. if (pendingCouponList[idx].auditStatus !== 'rejected') return { code: 400, data: null, msg: '仅审核拒绝的记录可删除' }
  1914. pendingCouponList.splice(idx, 1)
  1915. const aIdx = couponAuditList.findIndex(a => a.type === 'import' && a.batchId === body.batchId)
  1916. if (aIdx >= 0) couponAuditList.splice(aIdx, 1)
  1917. return { code: 200, data: null, msg: '已删除' }
  1918. })
  1919. export default Mock