Commit 2171bcc2f380de329ad1f9f61ae6bf8ce1c1abaf
1 parent
ac0a26c9
feat: card组件+filter组件+单选组件+缺省页+客户开发列表-list功能
Showing
20 changed files
with
1440 additions
and
41 deletions
api/base.js
0 → 100644
api/devManage.js
0 → 100644
| 1 | +import request from '@/utils/request' | ||
| 2 | +import { ContentTypeEnum } from '@/utils/httpEnum'; | ||
| 3 | + | ||
| 4 | +// 模拟字典接口,后续你可以替换为真实接口请求 utils/request.js | ||
| 5 | +export function getTabsDict() { | ||
| 6 | + return Promise.resolve([ | ||
| 7 | + { label: '全部', value: 'all' }, | ||
| 8 | + { label: '待办', value: 'todo' }, | ||
| 9 | + { label: '已办', value: 'done' } | ||
| 10 | + ]) | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +export function getFactoryDict() { | ||
| 14 | + return Promise.resolve([ | ||
| 15 | + { label: '全部', value: '' }, | ||
| 16 | + { label: '一、二分厂', value: '12' }, | ||
| 17 | + { label: '三、四分厂', value: '34' } | ||
| 18 | + ]) | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +export function getDeptOptionsDict() { | ||
| 22 | + return Promise.resolve([ | ||
| 23 | + { value: '一分厂', text: '一分厂' }, | ||
| 24 | + { value: '二分厂', text: '二分厂' }, | ||
| 25 | + { value: '三分厂', text: '三分厂' }, | ||
| 26 | + { value: '四分厂', text: '四分厂' }, | ||
| 27 | + { value: '四分厂2', text: '四分厂2' }, | ||
| 28 | + { value: '四分厂3', text: '四分厂3' }, | ||
| 29 | + { value: '四分厂4', text: '四分厂4' }, | ||
| 30 | + { value: '四分厂5', text: '四分厂5' } | ||
| 31 | + ]) | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +export const statusOptions = [ | ||
| 35 | + { value: 1, text: '审核中' }, | ||
| 36 | + { value: 2, text: '审核通过' }, | ||
| 37 | + { value: 3, text: '已驳回' }, | ||
| 38 | + { value: 4, text: '已取消' } | ||
| 39 | +]; | ||
| 40 | + | ||
| 41 | +const baseUrl = '/customer/develop'; | ||
| 42 | + | ||
| 43 | +// 查询列表 | ||
| 44 | +export function queryApi(params) { | ||
| 45 | + return request({ | ||
| 46 | + url: `${baseUrl}/query`, | ||
| 47 | + method: 'get', | ||
| 48 | + params | ||
| 49 | + }) | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +// 取消 | ||
| 53 | +export function cancelApi(id) { | ||
| 54 | + return request({ | ||
| 55 | + url: `${baseUrl}/cancel`, | ||
| 56 | + method: 'get', | ||
| 57 | + params: { id } | ||
| 58 | + }) | ||
| 59 | +} | ||
| 60 | +// 根据ID查询详情数据 | ||
| 61 | +export function getDetailApi(id) { | ||
| 62 | + return request({ | ||
| 63 | + url: `${baseUrl}`, | ||
| 64 | + method: 'get', | ||
| 65 | + params: { id } | ||
| 66 | + }) | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +// 新增保存 | ||
| 70 | +export function createApi(params) { | ||
| 71 | + return request({ | ||
| 72 | + url: `${baseUrl}`, | ||
| 73 | + method: 'post', | ||
| 74 | + data: params, | ||
| 75 | + contentType: ContentTypeEnum.FORM_URLENCODED | ||
| 76 | + }) | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | + | ||
| 80 | +// 修改保存 | ||
| 81 | +export function updateApi(params) { | ||
| 82 | + return request({ | ||
| 83 | + url: `${baseUrl}`, | ||
| 84 | + method: 'put', | ||
| 85 | + data: params, | ||
| 86 | + contentType: ContentTypeEnum.FORM_URLENCODED | ||
| 87 | + }) | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +// 批量审批 | ||
| 91 | +export function batchApproveApi(params) { | ||
| 92 | + return request({ | ||
| 93 | + url: `/flow/task/approve/batch`, | ||
| 94 | + method: 'post', | ||
| 95 | + data: params, | ||
| 96 | + }) | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +// 待办类型数量统计 | ||
| 100 | +export function getTodoTypeStatisticsApi() { | ||
| 101 | + return request({ | ||
| 102 | + url: `${baseUrl}/todoTypeStatistics`, | ||
| 103 | + method: 'get', | ||
| 104 | + }) | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +// 客户池查询 | ||
| 108 | +export function customerQueryApi(params) { | ||
| 109 | + return request({ | ||
| 110 | + url: `/basedata/customer/query`, | ||
| 111 | + method: 'get', | ||
| 112 | + params | ||
| 113 | + }) | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +// 科办查询 | ||
| 117 | +export function officeQueryApi(params) { | ||
| 118 | + return request({ | ||
| 119 | + url: `/baseData/office/query`, | ||
| 120 | + method: 'get', | ||
| 121 | + params | ||
| 122 | + }) | ||
| 123 | +} |
components/card/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view class="card-wrapper"> | ||
| 3 | + <scroll-view | ||
| 4 | + class="scroll" | ||
| 5 | + scroll-y | ||
| 6 | + :lower-threshold="lowerThreshold" | ||
| 7 | + :refresher-enabled="enableRefresh" | ||
| 8 | + :refresher-triggered="refresherTriggered" | ||
| 9 | + @refresherrefresh="onRefresh" | ||
| 10 | + @scrolltolower="onLoadMore" | ||
| 11 | + @scroll="onScroll" | ||
| 12 | + > | ||
| 13 | + <slot name="header"></slot> | ||
| 14 | + | ||
| 15 | + <view v-if="hasError && !loading" class="error"> | ||
| 16 | + <text>加载失败</text> | ||
| 17 | + <button class="retry" @click="reload">重试</button> | ||
| 18 | + </view> | ||
| 19 | + | ||
| 20 | + <Empty v-if="items.length === 0 && !loading && !hasError" text="暂无数据" /> | ||
| 21 | + | ||
| 22 | + <view | ||
| 23 | + v-for="(item, idx) in items" | ||
| 24 | + :key="getKey(item, idx)" | ||
| 25 | + :class="['card-item', { 'select-item': selectable }]" | ||
| 26 | + @click="toggleSelect(item, idx)" | ||
| 27 | + > | ||
| 28 | + <view v-if="selectable" class="check"> | ||
| 29 | + <view class="dot" :class="{ checked: isSelected(item, idx) }"> | ||
| 30 | + <uni-icons v-if="isSelected(item, idx)" type="checkmarkempty" color="#fff" size="14" /> | ||
| 31 | + </view> | ||
| 32 | + </view> | ||
| 33 | + <!-- 父组件传入的卡片内容 --> | ||
| 34 | + <slot :item="item" :selected="isSelected(item, idx)"></slot> | ||
| 35 | + </view> | ||
| 36 | + | ||
| 37 | + <!-- 当列表为空时不显示“没有更多数据了”,但加载时仍显示 loading --> | ||
| 38 | + <uni-load-more v-if="items.length > 0 || loading" :status="loadMoreStatus" /> | ||
| 39 | + </scroll-view> | ||
| 40 | + </view> | ||
| 41 | + | ||
| 42 | +</template> | ||
| 43 | + | ||
| 44 | +<script> | ||
| 45 | +export default { | ||
| 46 | + name: 'CardList', | ||
| 47 | + components: { Empty: () => import('../empty/index.vue') }, | ||
| 48 | + props: { | ||
| 49 | + // 请求方法,签名:({ pageIndex, pageSize, query, extra }) => Promise<{ records|list, totalCount }> | ||
| 50 | + fetchFn: { type: Function, default: null }, | ||
| 51 | + // 筛选条件对象(会被监听,变化后自动刷新) | ||
| 52 | + query: { type: Object, default: () => ({}) }, | ||
| 53 | + // 其他筛选值或附加参数(会被监听,变化后自动刷新) | ||
| 54 | + extra: { type: Object, default: () => ({}) }, | ||
| 55 | + pageSize: { type: Number, default: 10 }, | ||
| 56 | + immediate: { type: Boolean, default: true }, | ||
| 57 | + enableRefresh: { type: Boolean, default: true }, | ||
| 58 | + enableLoadMore: { type: Boolean, default: true }, | ||
| 59 | + // 支持多选 | ||
| 60 | + selectable: { type: Boolean, default: false }, | ||
| 61 | + rowKey: { type: String, default: 'id' }, | ||
| 62 | + // v-model:selectedKeys | ||
| 63 | + selectedKeys: { type: Array, default: () => [] } | ||
| 64 | + }, | ||
| 65 | + data() { | ||
| 66 | + return { | ||
| 67 | + items: [], | ||
| 68 | + pageIndex: 1, | ||
| 69 | + totalCount: 0, | ||
| 70 | + loading: false, | ||
| 71 | + finished: false, | ||
| 72 | + hasError: false, | ||
| 73 | + refresherTriggered: false, | ||
| 74 | + lowerThreshold: 120, | ||
| 75 | + scrollCooldownUntil: 0, | ||
| 76 | + loadMoreCooldownUntil: 0 | ||
| 77 | + } | ||
| 78 | + }, | ||
| 79 | + computed: { | ||
| 80 | + loadMoreStatus() { | ||
| 81 | + if (this.loading) return 'loading' | ||
| 82 | + if (this.finished) return 'noMore' | ||
| 83 | + return 'more' | ||
| 84 | + } | ||
| 85 | + }, | ||
| 86 | + watch: { | ||
| 87 | + query: { | ||
| 88 | + deep: true, | ||
| 89 | + handler() { | ||
| 90 | + this.reload() | ||
| 91 | + } | ||
| 92 | + }, | ||
| 93 | + extra: { | ||
| 94 | + deep: true, | ||
| 95 | + handler() { | ||
| 96 | + this.reload() | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + }, | ||
| 100 | + methods: { | ||
| 101 | + getKey(item, idx) { | ||
| 102 | + const k = this.rowKey && item && item[this.rowKey] != null ? item[this.rowKey] : idx | ||
| 103 | + return k | ||
| 104 | + }, | ||
| 105 | + isSelected(item, idx) { | ||
| 106 | + const key = this.getKey(item, idx) | ||
| 107 | + return this.selectedKeys.includes(key) | ||
| 108 | + }, | ||
| 109 | + toggleSelect(item, idx) { | ||
| 110 | + if (!this.selectable) return | ||
| 111 | + const key = this.getKey(item, idx) | ||
| 112 | + const next = this.selectedKeys.slice(0) | ||
| 113 | + const i = next.indexOf(key) | ||
| 114 | + if (i >= 0) next.splice(i, 1) | ||
| 115 | + else next.push(key) | ||
| 116 | + this.$emit('update:selectedKeys', next) | ||
| 117 | + }, | ||
| 118 | + onScroll(e) { | ||
| 119 | + if (!this.enableLoadMore || this.loading || this.finished) return | ||
| 120 | + // 刷新后的短暂冷却期内不触发,避免误触发加载更多 | ||
| 121 | + if (this.scrollCooldownUntil && Date.now() < this.scrollCooldownUntil) return | ||
| 122 | + const d = (e && e.detail) ? e.detail : {} | ||
| 123 | + const scrollTop = Number(d.scrollTop || 0) | ||
| 124 | + const scrollHeight = Number(d.scrollHeight || (e && e.target && e.target.scrollHeight) || 0) | ||
| 125 | + const clientHeight = Number(d.clientHeight || (e && e.target && e.target.clientHeight) || 0) | ||
| 126 | + const threshold = Number(this.lowerThreshold || 120) | ||
| 127 | + const canScroll = scrollHeight > clientHeight | ||
| 128 | + const nearBottom = scrollTop + clientHeight + threshold >= scrollHeight | ||
| 129 | + if (canScroll && nearBottom && scrollTop > 0) { | ||
| 130 | + this.onLoadMore() | ||
| 131 | + } | ||
| 132 | + }, | ||
| 133 | + async fetch() { | ||
| 134 | + if (!this.fetchFn) return | ||
| 135 | + this.loading = true | ||
| 136 | + this.hasError = false | ||
| 137 | + try { | ||
| 138 | + // 捕获请求时的页码,避免并发返回顺序导致错误的拼接/覆盖 | ||
| 139 | + const reqPage = this.pageIndex | ||
| 140 | + const res = await this.fetchFn({ | ||
| 141 | + pageIndex: reqPage, | ||
| 142 | + pageSize: this.pageSize, | ||
| 143 | + query: this.query, | ||
| 144 | + extra: this.extra | ||
| 145 | + }) | ||
| 146 | + const list = (res && (res.records || res.list || res.rows || res.items || res.datas)) | ||
| 147 | + ? (res.records || res.list || res.rows || res.items || res.datas) | ||
| 148 | + : []; | ||
| 149 | + const totalCount = (res && res.totalCount != null) ? res.totalCount : 0; | ||
| 150 | + // 根据请求发起时的页码判断赋值/拼接,避免错误追加旧数据 | ||
| 151 | + if (reqPage === 1) this.items = list | ||
| 152 | + else this.items = this.items.concat(list) | ||
| 153 | + this.totalCount = totalCount | ||
| 154 | + this.finished = (res && typeof res.hasNext === 'boolean') | ||
| 155 | + ? !res.hasNext | ||
| 156 | + : (this.items.length >= totalCount || list.length < this.pageSize) | ||
| 157 | + this.$emit('loaded', { items: this.items, totalCount: this.totalCount, pageIndex: this.pageIndex }) | ||
| 158 | + } catch (e) { | ||
| 159 | + console.error('[CardList] fetch error', e) | ||
| 160 | + this.hasError = true | ||
| 161 | + this.$emit('error', e) | ||
| 162 | + } finally { | ||
| 163 | + this.loading = false | ||
| 164 | + this.refresherTriggered = false | ||
| 165 | + } | ||
| 166 | + }, | ||
| 167 | + reload() { | ||
| 168 | + // 切换条件(如 tab/分厂)与刷新时,重置数据,不与之前请求数据拼接 | ||
| 169 | + this.pageIndex = 1 | ||
| 170 | + this.items = [] | ||
| 171 | + this.totalCount = 0 | ||
| 172 | + this.finished = false | ||
| 173 | + // 短暂禁止触发加载更多,避免滚动到底部立即追加 | ||
| 174 | + this.loadMoreCooldownUntil = Date.now() + 800 | ||
| 175 | + this.fetch() | ||
| 176 | + }, | ||
| 177 | + onRefresh() { | ||
| 178 | + if (!this.enableRefresh) return | ||
| 179 | + this.refresherTriggered = true | ||
| 180 | + // 设置滚动触发冷却期,避免刷新结束立刻触发加载更多 | ||
| 181 | + this.scrollCooldownUntil = Date.now() + 800 | ||
| 182 | + // 同步禁止 scrolltolower 的加载更多 | ||
| 183 | + this.loadMoreCooldownUntil = Date.now() + 800 | ||
| 184 | + this.reload() | ||
| 185 | + this.$emit('refresh') | ||
| 186 | + }, | ||
| 187 | + onLoadMore() { | ||
| 188 | + if (!this.enableLoadMore || this.loading || this.finished) return | ||
| 189 | + // 刷新/重载后的短暂冷却期,避免自动触底立即追加 | ||
| 190 | + if (this.loadMoreCooldownUntil && Date.now() < this.loadMoreCooldownUntil) return | ||
| 191 | + this.pageIndex += 1 | ||
| 192 | + this.fetch() | ||
| 193 | + }, | ||
| 194 | + clear() { | ||
| 195 | + this.items = [] | ||
| 196 | + this.totalCount = 0 | ||
| 197 | + this.pageIndex = 1 | ||
| 198 | + this.finished = false | ||
| 199 | + } | ||
| 200 | + }, | ||
| 201 | + mounted() { | ||
| 202 | + if (this.immediate) this.fetch() | ||
| 203 | + } | ||
| 204 | +} | ||
| 205 | +</script> | ||
| 206 | + | ||
| 207 | +<style lang="scss" scoped> | ||
| 208 | +@import '../../static/scss/global.scss'; | ||
| 209 | +.card-wrapper { | ||
| 210 | + display: flex; | ||
| 211 | + flex-direction: column; | ||
| 212 | + height: 100%; | ||
| 213 | +} | ||
| 214 | +.scroll { | ||
| 215 | + flex: 1; | ||
| 216 | + height: 100%; | ||
| 217 | +} | ||
| 218 | +.card-item { | ||
| 219 | + background: #fff; | ||
| 220 | + padding: 32rpx; | ||
| 221 | + position: relative; | ||
| 222 | + margin-bottom: 20rpx; | ||
| 223 | + &.select-item { | ||
| 224 | + left: 96rpx; | ||
| 225 | + } | ||
| 226 | +} | ||
| 227 | +.check { | ||
| 228 | + position: absolute; | ||
| 229 | + left: -60rpx; | ||
| 230 | + top: 50%; | ||
| 231 | + transform: translateY(-50%); | ||
| 232 | +} | ||
| 233 | +.dot { | ||
| 234 | + width: 32rpx; | ||
| 235 | + height: 32rpx; | ||
| 236 | + border-radius: 50%; | ||
| 237 | + border: 2rpx solid #dcdcdc; | ||
| 238 | + display: flex; | ||
| 239 | + align-items: center; | ||
| 240 | + justify-content: center; | ||
| 241 | +} | ||
| 242 | +.checked { | ||
| 243 | + background: $theme-primary; | ||
| 244 | + border-color: $theme-primary; | ||
| 245 | +} | ||
| 246 | +.error { | ||
| 247 | + display: flex; | ||
| 248 | + align-items: center; | ||
| 249 | + justify-content: center; | ||
| 250 | + gap: 20rpx; | ||
| 251 | + padding: 60rpx 0; | ||
| 252 | + color: #f56c6c; | ||
| 253 | +} | ||
| 254 | +.retry { | ||
| 255 | + border: 1rpx solid #f56c6c; | ||
| 256 | + color: #f56c6c; | ||
| 257 | + background: #fff; | ||
| 258 | +} | ||
| 259 | +</style> |
components/empty/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view class="empty-wrap"> | ||
| 3 | + <image class="empty-bg" src='/static/images/empty/empty_bg.png'/> | ||
| 4 | + <view class="empty-text"><text>{{ text }}</text></view> | ||
| 5 | + | ||
| 6 | + </view> | ||
| 7 | +</template> | ||
| 8 | + | ||
| 9 | +<script> | ||
| 10 | +export default { | ||
| 11 | + name: 'Empty', | ||
| 12 | + props: { | ||
| 13 | + text: { type: String, default: '暂无数据' } | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | +</script> | ||
| 17 | + | ||
| 18 | +<style scoped lang="scss"> | ||
| 19 | +.empty-wrap { | ||
| 20 | + padding: 240rpx 0; | ||
| 21 | + text-align: center; | ||
| 22 | + .empty-text { | ||
| 23 | + font-size: 26rpx; | ||
| 24 | + color: rgba(0,0,0,0.4); | ||
| 25 | + } | ||
| 26 | + .empty-bg { | ||
| 27 | + width: 440rpx; | ||
| 28 | + height: 282rpx; | ||
| 29 | + } | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +</style> |
components/filter/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <uni-popup ref="popup" type="bottom" :mask-click="false" :safe-area="true"> | ||
| 3 | + <view class="filter-modal"> | ||
| 4 | + <view class="header"> | ||
| 5 | + <text class="title">{{ title }}</text> | ||
| 6 | + <text class="close" @click="onClose">×</text> | ||
| 7 | + </view> | ||
| 8 | + | ||
| 9 | + <view class="body"> | ||
| 10 | + <!-- 父组件传入表单内容,通过作用域插槽拿到 model --> | ||
| 11 | + <slot :model="innerModel"></slot> | ||
| 12 | + </view> | ||
| 13 | + | ||
| 14 | + <view class="footer"> | ||
| 15 | + <button class="btn reset" @click="onReset">重置</button> | ||
| 16 | + <button class="btn confirm" type="primary" @click="onConfirm">确认</button> | ||
| 17 | + </view> | ||
| 18 | + </view> | ||
| 19 | + </uni-popup> | ||
| 20 | +</template> | ||
| 21 | + | ||
| 22 | +<script> | ||
| 23 | +export default { | ||
| 24 | + name: 'FilterModal', | ||
| 25 | + props: { | ||
| 26 | + // v-model:visible 控制显示;初始值 | ||
| 27 | + visible: { type: Boolean, default: false }, | ||
| 28 | + // v-model:value 过滤表单对象 | ||
| 29 | + value: { type: Object, default: () => ({}) }, | ||
| 30 | + title: { type: String, default: '筛选' } | ||
| 31 | + }, | ||
| 32 | + data() { | ||
| 33 | + return { | ||
| 34 | + innerModel: JSON.parse(JSON.stringify(this.value || {})) | ||
| 35 | + } | ||
| 36 | + }, | ||
| 37 | + watch: { | ||
| 38 | + value: { | ||
| 39 | + deep: true, | ||
| 40 | + handler(v) { | ||
| 41 | + this.innerModel = JSON.parse(JSON.stringify(v || {})) | ||
| 42 | + } | ||
| 43 | + }, | ||
| 44 | + visible(v) { | ||
| 45 | + if (v) this.open() | ||
| 46 | + else this.close() | ||
| 47 | + } | ||
| 48 | + }, | ||
| 49 | + mounted() { | ||
| 50 | + if (this.visible) this.open() | ||
| 51 | + }, | ||
| 52 | + methods: { | ||
| 53 | + open() { | ||
| 54 | + this.$refs.popup && this.$refs.popup.open() | ||
| 55 | + this.$emit('update:visible', true) | ||
| 56 | + }, | ||
| 57 | + close() { | ||
| 58 | + this.$refs.popup && this.$refs.popup.close() | ||
| 59 | + this.$emit('update:visible', false) | ||
| 60 | + }, | ||
| 61 | + onClose() { | ||
| 62 | + this.close() | ||
| 63 | + this.$emit('close') | ||
| 64 | + }, | ||
| 65 | + onReset() { | ||
| 66 | + const m = this.innerModel || {} | ||
| 67 | + Object.keys(m).forEach(k => { | ||
| 68 | + const val = m[k] | ||
| 69 | + if (Array.isArray(val)) m[k] = [] | ||
| 70 | + else if (val && typeof val === 'object') m[k] = null | ||
| 71 | + else m[k] = '' | ||
| 72 | + }) | ||
| 73 | + // 不关闭弹框,仅告知父组件当前值 | ||
| 74 | + this.$emit('reset', { ...m }) | ||
| 75 | + this.$emit('update:value', { ...m }) | ||
| 76 | + }, | ||
| 77 | + onConfirm() { | ||
| 78 | + const payload = { ...this.innerModel } | ||
| 79 | + this.$emit('confirm', payload) | ||
| 80 | + this.$emit('input', payload) | ||
| 81 | + this.$emit('update:value', payload) | ||
| 82 | + this.close() | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | +} | ||
| 86 | +</script> | ||
| 87 | + | ||
| 88 | +<style lang="scss" scoped> | ||
| 89 | +.filter-modal { | ||
| 90 | + width: 100%; | ||
| 91 | + max-height: 70vh; | ||
| 92 | + min-height: 40vh; | ||
| 93 | + background: #fff; | ||
| 94 | + border-radius: 20rpx 20rpx 0 0; | ||
| 95 | + overflow: hidden; | ||
| 96 | + display: flex; | ||
| 97 | + flex-direction: column; | ||
| 98 | +} | ||
| 99 | +.header { | ||
| 100 | + flex: 0 0 auto; | ||
| 101 | + display: flex; | ||
| 102 | + align-items: center; | ||
| 103 | + justify-content: space-between; | ||
| 104 | + padding: 24rpx 28rpx; | ||
| 105 | + border-bottom: 1rpx solid #f0f0f0; | ||
| 106 | +} | ||
| 107 | +.title { | ||
| 108 | + font-size: 36rpx; | ||
| 109 | + font-weight: 600; | ||
| 110 | + flex: 1; | ||
| 111 | + text-align: center; | ||
| 112 | +} | ||
| 113 | +.close { | ||
| 114 | + font-size: 36rpx; | ||
| 115 | + color: #666; | ||
| 116 | +} | ||
| 117 | +.body { | ||
| 118 | + flex: 1 1 auto; | ||
| 119 | + padding: 32rpx 32rpx 48rpx; | ||
| 120 | + overflow-y: auto; | ||
| 121 | +} | ||
| 122 | +.footer { | ||
| 123 | + flex: 0 0 auto; | ||
| 124 | + display: flex; | ||
| 125 | + gap: 32rpx; | ||
| 126 | + padding: 32rpx; | ||
| 127 | + border-top: 1rpx solid #f5f5f5; | ||
| 128 | +} | ||
| 129 | +.btn { | ||
| 130 | + flex: 1; | ||
| 131 | + height: 80rpx; | ||
| 132 | + line-height: 80rpx; | ||
| 133 | + border-radius: 12rpx; | ||
| 134 | + font-size: 32rpx; | ||
| 135 | +} | ||
| 136 | +.reset { | ||
| 137 | + background: $theme-primary-plain-bg; | ||
| 138 | + color: $theme-primary; | ||
| 139 | + &::after { | ||
| 140 | + border: none; | ||
| 141 | + } | ||
| 142 | +} | ||
| 143 | +.confirm { | ||
| 144 | + background: $theme-primary; | ||
| 145 | + color: #fff; | ||
| 146 | +} | ||
| 147 | +</style> |
components/single-select/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <uni-popup ref="popup" type="bottom" :mask-click="false" :safe-area="true"> | ||
| 3 | + <view class="sheet"> | ||
| 4 | + <view class="sheet-header"> | ||
| 5 | + <text class="cancel" @click="onCancel">取消</text> | ||
| 6 | + <text class="title">{{ title }}</text> | ||
| 7 | + <text class="ok" @click="onConfirm">确认</text> | ||
| 8 | + </view> | ||
| 9 | + <view class="sheet-body"> | ||
| 10 | + <view | ||
| 11 | + v-for="(opt, i) in options" | ||
| 12 | + :key="i" | ||
| 13 | + :class="['option', { selected: isSelected(opt) }]" | ||
| 14 | + @click="select(opt)" | ||
| 15 | + > | ||
| 16 | + <text class="label">{{ displayOf(opt) }}</text> | ||
| 17 | + </view> | ||
| 18 | + </view> | ||
| 19 | + </view> | ||
| 20 | + </uni-popup> | ||
| 21 | +</template> | ||
| 22 | + | ||
| 23 | +<script> | ||
| 24 | +export default { | ||
| 25 | + name: 'SingleSelectSheet', | ||
| 26 | + props: { | ||
| 27 | + visible: { type: Boolean, default: false }, | ||
| 28 | + title: { type: String, default: '请选择' }, | ||
| 29 | + // 统一约定:options 为 [{ label: string, value: string|number }] | ||
| 30 | + options: { type: Array, default: () => [] }, | ||
| 31 | + value: { type: [String, Number], default: '' } | ||
| 32 | + }, | ||
| 33 | + data() { | ||
| 34 | + return { | ||
| 35 | + innerValue: this.value, | ||
| 36 | + innerOption: null | ||
| 37 | + } | ||
| 38 | + }, | ||
| 39 | + watch: { | ||
| 40 | + value(v) { this.innerValue = v }, | ||
| 41 | + visible(v) { v ? this.open() : this.close() } | ||
| 42 | + }, | ||
| 43 | + mounted() { | ||
| 44 | + if (this.visible) this.open() | ||
| 45 | + }, | ||
| 46 | + methods: { | ||
| 47 | + open() { | ||
| 48 | + // 打开弹框时回显当前已选中的值 | ||
| 49 | + this.innerValue = this.value | ||
| 50 | + this.innerOption = (this.options || []).find(o => this.valueOf(o) === this.value) || null | ||
| 51 | + this.$refs.popup && this.$refs.popup.open() | ||
| 52 | + this.$emit('update:visible', true) | ||
| 53 | + }, | ||
| 54 | + close() { | ||
| 55 | + // 关闭时同步回显值,避免取消后残留未确认值 | ||
| 56 | + this.innerValue = this.value | ||
| 57 | + this.$refs.popup && this.$refs.popup.close() | ||
| 58 | + this.$emit('update:visible', false) | ||
| 59 | + }, | ||
| 60 | + displayOf(opt) { | ||
| 61 | + if (!opt) return '' | ||
| 62 | + return opt.label != null ? String(opt.label) : '' | ||
| 63 | + }, | ||
| 64 | + valueOf(opt) { | ||
| 65 | + if (!opt) return '' | ||
| 66 | + return opt.value != null ? opt.value : '' | ||
| 67 | + }, | ||
| 68 | + isSelected(opt) { | ||
| 69 | + return this.valueOf(opt) === this.innerValue | ||
| 70 | + }, | ||
| 71 | + select(opt) { | ||
| 72 | + this.innerValue = this.valueOf(opt) | ||
| 73 | + this.innerOption = opt | ||
| 74 | + }, | ||
| 75 | + onCancel() { | ||
| 76 | + this.close() | ||
| 77 | + this.$emit('cancel') | ||
| 78 | + }, | ||
| 79 | + onConfirm() { | ||
| 80 | + const val = this.innerValue | ||
| 81 | + const opt = this.innerOption || (this.options || []).find(o => this.valueOf(o) === val) || null | ||
| 82 | + const label = opt ? this.displayOf(opt) : '' | ||
| 83 | + this.$emit('confirm', { value: val, label }) | ||
| 84 | + this.$emit('input', val) | ||
| 85 | + this.$emit('update:value', val) | ||
| 86 | + this.close() | ||
| 87 | + } | ||
| 88 | + } | ||
| 89 | +} | ||
| 90 | +</script> | ||
| 91 | + | ||
| 92 | +<style lang="scss" scoped> | ||
| 93 | +.sheet { | ||
| 94 | + width: 100%; | ||
| 95 | + height: 45vh; | ||
| 96 | + background: #fff; | ||
| 97 | + border-radius: 20rpx 20rpx 0 0; | ||
| 98 | + display: flex; | ||
| 99 | + flex-direction: column; | ||
| 100 | +} | ||
| 101 | +.sheet-header { | ||
| 102 | + display: flex; | ||
| 103 | + align-items: center; | ||
| 104 | + justify-content: space-between; | ||
| 105 | + padding: 30rpx 32rpx; | ||
| 106 | + border-bottom: 1rpx solid #f0f0f0; | ||
| 107 | +} | ||
| 108 | +.title { | ||
| 109 | + font-size: 36rpx; | ||
| 110 | + font-weight: 600; | ||
| 111 | +} | ||
| 112 | +.cancel { | ||
| 113 | + color: rgba(0,0,0,0.6); | ||
| 114 | + font-size: 28rpx; | ||
| 115 | +} | ||
| 116 | +.ok { | ||
| 117 | + color: $theme-primary; | ||
| 118 | + font-size: 28rpx; | ||
| 119 | +} | ||
| 120 | +.sheet-body { | ||
| 121 | + flex: 1 1 auto; | ||
| 122 | + overflow-y: auto; | ||
| 123 | + padding: 32rpx; | ||
| 124 | +} | ||
| 125 | +.option { | ||
| 126 | + height: 80rpx; | ||
| 127 | + line-height: 80rpx; | ||
| 128 | + padding: 0 20rpx; | ||
| 129 | + background: #fff; | ||
| 130 | + text-align: center; | ||
| 131 | + border-radius: 12rpx; | ||
| 132 | + font-size: 32rpx; | ||
| 133 | + .label { | ||
| 134 | + color: rgba(0,0,0,0.6); | ||
| 135 | + font-size: 32rpx; | ||
| 136 | + } | ||
| 137 | + &.selected { | ||
| 138 | + background: #f3f3f3; | ||
| 139 | + .label { | ||
| 140 | + color: rgba(0,0,0,0.9); | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +</style> |
| @@ -17,22 +17,6 @@ | @@ -17,22 +17,6 @@ | ||
| 17 | "navigationStyle": "custom" | 17 | "navigationStyle": "custom" |
| 18 | } | 18 | } |
| 19 | }, { | 19 | }, { |
| 20 | - "path": "pages/data/index", | ||
| 21 | - "style": { | ||
| 22 | - "navigationBarTitleText": "资料" | ||
| 23 | - } | ||
| 24 | - }, { | ||
| 25 | - "path": "pages/business/index", | ||
| 26 | - "style": { | ||
| 27 | - "navigationBarTitleText": "业务" | ||
| 28 | - } | ||
| 29 | - }, { | ||
| 30 | - "path": "pages/report/index", | ||
| 31 | - "style": { | ||
| 32 | - "navigationBarTitleText": "财务" | ||
| 33 | - | ||
| 34 | - } | ||
| 35 | - }, { | ||
| 36 | "path": "pages/mine/index", | 20 | "path": "pages/mine/index", |
| 37 | "style": { | 21 | "style": { |
| 38 | "navigationBarTitleText": "我的", | 22 | "navigationBarTitleText": "我的", |
| @@ -44,21 +28,11 @@ | @@ -44,21 +28,11 @@ | ||
| 44 | "navigationBarTitleText": "我的2" | 28 | "navigationBarTitleText": "我的2" |
| 45 | } | 29 | } |
| 46 | }, { | 30 | }, { |
| 47 | - "path": "pages/mine/avatar/index", | ||
| 48 | - "style": { | ||
| 49 | - "navigationBarTitleText": "修改头像" | ||
| 50 | - } | ||
| 51 | - }, { | ||
| 52 | "path": "pages/mine/info/index", | 31 | "path": "pages/mine/info/index", |
| 53 | "style": { | 32 | "style": { |
| 54 | "navigationBarTitleText": "个人信息" | 33 | "navigationBarTitleText": "个人信息" |
| 55 | } | 34 | } |
| 56 | }, { | 35 | }, { |
| 57 | - "path": "pages/mine/info/edit", | ||
| 58 | - "style": { | ||
| 59 | - "navigationBarTitleText": "编辑资料" | ||
| 60 | - } | ||
| 61 | - }, { | ||
| 62 | "path": "pages/mine/pwd/index", | 36 | "path": "pages/mine/pwd/index", |
| 63 | "style": { | 37 | "style": { |
| 64 | "navigationBarTitleText": "修改密码" | 38 | "navigationBarTitleText": "修改密码" |
| @@ -69,24 +43,16 @@ | @@ -69,24 +43,16 @@ | ||
| 69 | "navigationBarTitleText": "应用设置" | 43 | "navigationBarTitleText": "应用设置" |
| 70 | } | 44 | } |
| 71 | }, { | 45 | }, { |
| 72 | - "path": "pages/mine/help/index", | ||
| 73 | - "style": { | ||
| 74 | - "navigationBarTitleText": "常见问题" | ||
| 75 | - } | ||
| 76 | - }, { | ||
| 77 | "path": "pages/mine/about/index", | 46 | "path": "pages/mine/about/index", |
| 78 | "style": { | 47 | "style": { |
| 79 | "navigationBarTitleText": "关于我们" | 48 | "navigationBarTitleText": "关于我们" |
| 80 | } | 49 | } |
| 81 | }, { | 50 | }, { |
| 82 | - "path": "pages/common/webview/index", | ||
| 83 | - "style": { | ||
| 84 | - "navigationBarTitleText": "浏览网页" | ||
| 85 | - } | ||
| 86 | - }, { | ||
| 87 | - "path": "pages/common/textview/index", | 51 | + "path": "pages/dev_manage/index", |
| 88 | "style": { | 52 | "style": { |
| 89 | - "navigationBarTitleText": "浏览文本" | 53 | + "navigationBarTitleText": "客户开发管理", |
| 54 | + "navigationBarBackgroundColor": "#ffffff", | ||
| 55 | + "navigationBarTextStyle": "black" | ||
| 90 | } | 56 | } |
| 91 | } | 57 | } |
| 92 | ], | 58 | ], |
pages/dev_manage/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <view class="page"> | ||
| 3 | + <view class="dev-list-fixed"> | ||
| 4 | + <view class="search-row"> | ||
| 5 | + <uni-search-bar | ||
| 6 | + v-model="searchKeyword" | ||
| 7 | + radius="6" | ||
| 8 | + placeholder="请输入客户名称" | ||
| 9 | + clearButton="auto" | ||
| 10 | + cancelButton="none" | ||
| 11 | + bgColor="#F3F3F3" | ||
| 12 | + textColor="rgba(0,0,0,0.4)" | ||
| 13 | + @confirm="search" | ||
| 14 | + @input="onSearchInput" | ||
| 15 | + /> | ||
| 16 | + <view class="tool-icons"> | ||
| 17 | + <image class="tool-icon" | ||
| 18 | + :src="batchMode ? '/static/images/dev_manage/close_icon.png' : '/static/images/dev_manage/batch_icon.png'" | ||
| 19 | + @click="toggleBatch" | ||
| 20 | + /> | ||
| 21 | + <image class="tool-icon" src="/static/images/dev_manage/add_icon.png" @click="onAdd" /> | ||
| 22 | + <image class="tool-icon" src="/static/images/dev_manage/filter_icon.png" @click="openFilter" /> | ||
| 23 | + </view> | ||
| 24 | + </view> | ||
| 25 | + | ||
| 26 | + <!-- 页内 tabs --> | ||
| 27 | + <view class="tabs"> | ||
| 28 | + <view | ||
| 29 | + v-for="(t, i) in tabs" | ||
| 30 | + :key="i" | ||
| 31 | + :class="['tab', { active: todoType === t.value }]" | ||
| 32 | + @click="switchTab(t)" | ||
| 33 | + > | ||
| 34 | + {{ t.label }} | ||
| 35 | + </view> | ||
| 36 | + </view> | ||
| 37 | + <view :class="['sub-tabs', `bg${currentWorkshopTypeIndex + 1}`]"> | ||
| 38 | + <view | ||
| 39 | + v-for="(f, i) in workshopTypeList" | ||
| 40 | + :key="i" | ||
| 41 | + :class="['tab', { active: workshopType === f.value }]" | ||
| 42 | + @click="switchWorkshopType(f, i)" | ||
| 43 | + > | ||
| 44 | + {{ f.label }} | ||
| 45 | + </view> | ||
| 46 | + </view> | ||
| 47 | + </view> | ||
| 48 | + | ||
| 49 | + | ||
| 50 | + <!-- 列表卡片组件 --> | ||
| 51 | + <view :class="['list-box', { 'pad-batch': batchMode }]"> | ||
| 52 | + <card-list | ||
| 53 | + ref="cardRef" | ||
| 54 | + :fetch-fn="fetchList" | ||
| 55 | + :query="query" | ||
| 56 | + :extra="extraParams" | ||
| 57 | + :selectable="batchMode" | ||
| 58 | + row-key="id" | ||
| 59 | + :selectedKeys.sync="selectedKeys" | ||
| 60 | + :enable-refresh="true" | ||
| 61 | + :enable-load-more="true" | ||
| 62 | + @loaded="onCardLoaded" | ||
| 63 | + @error="onCardError" | ||
| 64 | + > | ||
| 65 | + <template v-slot="{ item, selected }"> | ||
| 66 | + <view class="card"> | ||
| 67 | + <view class="card-header"> | ||
| 68 | + <text class="title omit2">{{ item.customer.name }}</text> | ||
| 69 | + <text :class="['status', `status_${item.status}`]">{{ filterStatus(item.status) }}</text> | ||
| 70 | + </view> | ||
| 71 | + <view class="info-row"> | ||
| 72 | + <text>生产厂</text><text>{{ item.workshop.name }}</text> | ||
| 73 | + </view> | ||
| 74 | + <view class="info-row"> | ||
| 75 | + <text>科办</text><text>{{ item.office.name }}</text> | ||
| 76 | + </view> | ||
| 77 | + <view class="info-row"> | ||
| 78 | + <text>产品品种</text><text>{{ item.productVariety.name }}</text> | ||
| 79 | + </view> | ||
| 80 | + <view class="info-row"> | ||
| 81 | + <text>创建时间</text><text>{{ item.createTime }}</text> | ||
| 82 | + </view> | ||
| 83 | + </view> | ||
| 84 | + </template> | ||
| 85 | + </card-list> | ||
| 86 | + </view> | ||
| 87 | + | ||
| 88 | + <!-- 批量操作条 --> | ||
| 89 | + <view v-if="batchMode" class="batch-bar"> | ||
| 90 | + <button class="bbtn reject" @click="batchReject">驳回</button> | ||
| 91 | + <button class="bbtn" type="primary" @click="batchPass">通过</button> | ||
| 92 | + </view> | ||
| 93 | + | ||
| 94 | + <!-- 筛选弹框 --> | ||
| 95 | + <filter-modal | ||
| 96 | + :visible.sync="filterVisible" | ||
| 97 | + :value.sync="filterForm" | ||
| 98 | + title="筛选" | ||
| 99 | + @reset="onFilterReset" | ||
| 100 | + @confirm="onFilterConfirm" | ||
| 101 | + > | ||
| 102 | + <template v-slot="{ model }"> | ||
| 103 | + <view class="filter-form"> | ||
| 104 | + <view class="form-item"> | ||
| 105 | + <view class="label">审核状态</view> | ||
| 106 | + <uni-data-checkbox | ||
| 107 | + mode="tag" | ||
| 108 | + :multiple="false" | ||
| 109 | + :value-field="'value'" | ||
| 110 | + :text-field="'text'" | ||
| 111 | + v-model="model.status" | ||
| 112 | + @change="onStatusChange" | ||
| 113 | + :localdata="statusOptions" | ||
| 114 | + /> | ||
| 115 | + </view> | ||
| 116 | + <view class="form-item"> | ||
| 117 | + <view class="label">科办</view> | ||
| 118 | + <view class="fake-select" @click="openOfficeSelect"> | ||
| 119 | + <text v-if="!model.officeId" class="placeholder">请选择</text> | ||
| 120 | + <text v-else class="value">{{ model.officeName }}</text> | ||
| 121 | + </view> | ||
| 122 | + </view> | ||
| 123 | + <view class="form-item"> | ||
| 124 | + <view class="label">创建时间</view> | ||
| 125 | + <uni-datetime-picker type="daterange" v-model="model.dateRange" start="2023-01-01" /> | ||
| 126 | + </view> | ||
| 127 | + </view> | ||
| 128 | + </template> | ||
| 129 | + </filter-modal> | ||
| 130 | + <!-- 科办单选底部弹层(通用组件) --> | ||
| 131 | + <single-select-sheet | ||
| 132 | + :visible.sync="officeSelectVisible" | ||
| 133 | + :options="officeList" | ||
| 134 | + :value.sync="filterForm.officeId" | ||
| 135 | + title="科办" | ||
| 136 | + @confirm="onOfficeConfirm" | ||
| 137 | + /> | ||
| 138 | + </view> | ||
| 139 | +</template> | ||
| 140 | + | ||
| 141 | +<script> | ||
| 142 | +import CardList from '@/components/card/index.vue' | ||
| 143 | +import FilterModal from '@/components/filter/index.vue' | ||
| 144 | +import SingleSelectSheet from '@/components/single-select/index.vue' | ||
| 145 | +import { queryApi, officeQueryApi, statusOptions, getTodoTypeStatisticsApi } from '@/api/devManage.js' | ||
| 146 | +import {getDicByCodes, getDicName} from '@/utils/dic'; | ||
| 147 | + | ||
| 148 | +export default { | ||
| 149 | + components: { CardList, FilterModal, SingleSelectSheet }, | ||
| 150 | + data() { | ||
| 151 | + return { | ||
| 152 | + searchKeyword: '', | ||
| 153 | + officeList: [], // 科办列表下拉选 | ||
| 154 | + tabs: [], | ||
| 155 | + todoType: '', | ||
| 156 | + workshopTypeList: [ | ||
| 157 | + { | ||
| 158 | + label: '全部', | ||
| 159 | + value: '' | ||
| 160 | + }, | ||
| 161 | + { | ||
| 162 | + label: '一、二分厂', | ||
| 163 | + value: 'TYPE_1' | ||
| 164 | + }, | ||
| 165 | + { | ||
| 166 | + label: '三、四分厂', | ||
| 167 | + value: 'TYPE_2' | ||
| 168 | + } | ||
| 169 | + ], | ||
| 170 | + workshopType: '', | ||
| 171 | + currentWorkshopTypeIndex: 0, | ||
| 172 | + // 字典选项缓存 | ||
| 173 | + // dicOptions: { | ||
| 174 | + // ENTERPRISE_TYPE: [], // 客户类型 | ||
| 175 | + // }, | ||
| 176 | + // 给到 card 的筛选值 | ||
| 177 | + query: { status: '', officeId: '', officeName: '', dateRange: [] }, | ||
| 178 | + extraParams: {}, | ||
| 179 | + | ||
| 180 | + // 批量选择 | ||
| 181 | + batchMode: false, | ||
| 182 | + selectedKeys: [], | ||
| 183 | + rowKey: 'customerId', | ||
| 184 | + | ||
| 185 | + // 筛选弹框 | ||
| 186 | + filterVisible: false, | ||
| 187 | + filterForm: { status: '', officeId: '', officeName: '', dateRange: [] }, | ||
| 188 | + officeSelectVisible: false, | ||
| 189 | + // 审核状态枚举(来自 api/devManage.js) | ||
| 190 | + statusOptions, | ||
| 191 | + } | ||
| 192 | + }, | ||
| 193 | + computed: { | ||
| 194 | + extraCombined() { | ||
| 195 | + return { | ||
| 196 | + keyword: this.searchKeyword, | ||
| 197 | + todoType: this.todoType || undefined, | ||
| 198 | + workshopType: this.workshopType || undefined | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | + }, | ||
| 202 | + watch: { | ||
| 203 | + extraCombined: { | ||
| 204 | + deep: true, | ||
| 205 | + handler(v) { | ||
| 206 | + this.extraParams = v | ||
| 207 | + }, | ||
| 208 | + immediate: true | ||
| 209 | + } | ||
| 210 | + }, | ||
| 211 | + created() { | ||
| 212 | + this.getOffice(); | ||
| 213 | + // this.loadAllDicData(); | ||
| 214 | + // 获取待办类型数量统计 | ||
| 215 | + this.getTodoTypeStatisticsFun() | ||
| 216 | + }, | ||
| 217 | + // 页面触底兜底:当页面自身滚动到底部时,转调卡片组件加载更多 | ||
| 218 | + onReachBottom() { | ||
| 219 | + if (this.$refs && this.$refs.cardRef && this.$refs.cardRef.onLoadMore) { | ||
| 220 | + this.$refs.cardRef.onLoadMore() | ||
| 221 | + } | ||
| 222 | + }, | ||
| 223 | + beforeDestroy() { | ||
| 224 | + if (this.searchDebounceTimer) { | ||
| 225 | + clearTimeout(this.searchDebounceTimer) | ||
| 226 | + this.searchDebounceTimer = null | ||
| 227 | + } | ||
| 228 | + }, | ||
| 229 | + methods: { | ||
| 230 | + onCardLoaded({ items }) { | ||
| 231 | + this.currentItems = items | ||
| 232 | + }, | ||
| 233 | + onCardError() { | ||
| 234 | + uni.showToast({ title: '列表加载失败', icon: 'none' }) | ||
| 235 | + }, | ||
| 236 | + onSearch() { | ||
| 237 | + this.extraParams = { ...this.extraParams, keyword: this.searchKeyword } | ||
| 238 | + }, | ||
| 239 | + // 输入实时搜索:300ms 防抖更新关键字并触发 CardList 刷新 | ||
| 240 | + onSearchInput(val) { | ||
| 241 | + this.searchKeyword = typeof val === 'string' ? val : (val && val.value) ? val.value : this.searchKeyword | ||
| 242 | + if (this.searchDebounceTimer) clearTimeout(this.searchDebounceTimer) | ||
| 243 | + this.searchDebounceTimer = setTimeout(() => { | ||
| 244 | + this.extraParams = { ...this.extraParams, keyword: this.searchKeyword } | ||
| 245 | + this.searchDebounceTimer = null | ||
| 246 | + }, 300) | ||
| 247 | + }, | ||
| 248 | + // uni-search-bar 确认搜索:更新关键字并触发 CardList 刷新 | ||
| 249 | + search(e) { | ||
| 250 | + const val = e && e.value != null ? e.value : this.searchKeyword | ||
| 251 | + this.searchKeyword = val | ||
| 252 | + this.extraParams = { ...this.extraParams, keyword: val } | ||
| 253 | + }, | ||
| 254 | + switchTab(item) { | ||
| 255 | + this.todoType = item.value; | ||
| 256 | + this.extraParams = { ...this.extraParams, todoType: item.value } | ||
| 257 | + }, | ||
| 258 | + switchWorkshopType(item, i) { | ||
| 259 | + this.workshopType = item.value; | ||
| 260 | + this.currentWorkshopTypeIndex = i; | ||
| 261 | + this.extraParams = { ...this.extraParams, workshopType: item.value } | ||
| 262 | + }, | ||
| 263 | + openFilter() { | ||
| 264 | + this.filterVisible = true | ||
| 265 | + }, | ||
| 266 | + onFilterReset(payload) { | ||
| 267 | + console.log('onFilterReset',payload) | ||
| 268 | + // 保持弹框不关闭,仅同步表单 | ||
| 269 | + this.filterForm = payload | ||
| 270 | + }, | ||
| 271 | + onFilterConfirm(payload) { | ||
| 272 | + // 兜底:部分端上 uni-data-checkbox(mode=tag) 不触发 v-model | ||
| 273 | + if ((payload.status === '' || payload.status == null) && this.filterForm.status !== '') { | ||
| 274 | + payload.status = this.filterForm.status | ||
| 275 | + } | ||
| 276 | + console.log('onFilterConfirm',payload) | ||
| 277 | + // 关闭弹框,父组件给到 card 的条件 | ||
| 278 | + this.query = { ...payload } | ||
| 279 | + }, | ||
| 280 | + onStatusChange(e) { | ||
| 281 | + const raw = e && e.detail && e.detail.value !== undefined | ||
| 282 | + ? e.detail.value | ||
| 283 | + : (e && e.value !== undefined ? e.value : '') | ||
| 284 | + // 直接同步到外层 filterForm,驱动 FilterModal 的 innerModel 更新 | ||
| 285 | + this.filterForm.status = raw | ||
| 286 | + }, | ||
| 287 | + openOfficeSelect() { | ||
| 288 | + this.officeSelectVisible = true | ||
| 289 | + }, | ||
| 290 | + onOfficeConfirm(payload) { | ||
| 291 | + const val = payload && payload.value != null ? payload.value : '' | ||
| 292 | + const label = payload && payload.label != null ? payload.label : '' | ||
| 293 | + this.filterForm.officeId = val | ||
| 294 | + this.filterForm.officeName = label | ||
| 295 | + }, | ||
| 296 | + toggleBatch() { | ||
| 297 | + this.batchMode = !this.batchMode | ||
| 298 | + if (!this.batchMode) this.selectedKeys = [] | ||
| 299 | + }, | ||
| 300 | + batchReject() { | ||
| 301 | + if (this.selectedKeys.length === 0) return uni.showToast({ title: '请选择记录', icon: 'none' }) | ||
| 302 | + uni.showToast({ title: '已发起驳回', icon: 'none' }) | ||
| 303 | + }, | ||
| 304 | + batchPass() { | ||
| 305 | + if (this.selectedKeys.length === 0) return uni.showToast({ title: '请选择记录', icon: 'none' }) | ||
| 306 | + uni.showToast({ title: '已发起通过', icon: 'none' }) | ||
| 307 | + }, | ||
| 308 | + onAdd() { | ||
| 309 | + uni.showToast({ title: '点击新增', icon: 'none' }) | ||
| 310 | + }, | ||
| 311 | + // 列表接口(真实请求) | ||
| 312 | + fetchList({ pageIndex, pageSize, query, extra }) { | ||
| 313 | + const params = { pageIndex, pageSize, ...extra, ...query } | ||
| 314 | + // 处理日期范围 | ||
| 315 | + if (Array.isArray(params.dateRange) && params.dateRange.length === 2) { | ||
| 316 | + params.createStartTime = params.dateRange[0] + ' 00:00:00' | ||
| 317 | + params.createEndTime = params.dateRange[1] + ' 23:59:59' | ||
| 318 | + delete params.dateRange | ||
| 319 | + } | ||
| 320 | + // 关键字 | ||
| 321 | + if (this.searchKeyword) { | ||
| 322 | + params.keyword = this.searchKeyword | ||
| 323 | + } | ||
| 324 | + return queryApi(params) | ||
| 325 | + .then(res => { | ||
| 326 | + console.log('fetchList', res) | ||
| 327 | + const _data = res.data || {}; | ||
| 328 | + const records = _data.datas || []; | ||
| 329 | + const totalCount = _data.totalCount || 0; | ||
| 330 | + const hasNext = _data.hasNext || false | ||
| 331 | + return { records, totalCount, hasNext } | ||
| 332 | + }) | ||
| 333 | + .catch(err => { | ||
| 334 | + console.error('fetchList error', err) | ||
| 335 | + this.onCardError() | ||
| 336 | + return { records: [], totalCount: 0, hasNext: false } | ||
| 337 | + }) | ||
| 338 | + }, | ||
| 339 | + getOffice() { | ||
| 340 | + officeQueryApi({ | ||
| 341 | + pageIndex: 1, | ||
| 342 | + pageSize: 9999, | ||
| 343 | + }).then((res) => { | ||
| 344 | + const _data = res.data.datas.map(item => { | ||
| 345 | + return { | ||
| 346 | + label: item.name, | ||
| 347 | + value: item.id | ||
| 348 | + } | ||
| 349 | + }) || [] | ||
| 350 | + console.log('officeList_data', _data) | ||
| 351 | + this.officeList = _data; | ||
| 352 | + }); | ||
| 353 | + }, | ||
| 354 | + getTodoTypeStatisticsFun() { | ||
| 355 | + getTodoTypeStatisticsApi().then((res) => { | ||
| 356 | + this.tabs = [ | ||
| 357 | + { | ||
| 358 | + label: `全部(${res.data.allCount > 999 ? '999+' : res.data.allCount || 0})`, | ||
| 359 | + value: '' | ||
| 360 | + }, | ||
| 361 | + { | ||
| 362 | + label: `已办(${res.data.completedCount > 999 ? '999+' : res.data.completedCount || 0})`, | ||
| 363 | + value: 'COMPLETED' | ||
| 364 | + }, | ||
| 365 | + { | ||
| 366 | + label: `待办(${res.data.todoCount > 999 ? '999+' : res.data.todoCount || 0})`, | ||
| 367 | + value: 'WAIT' | ||
| 368 | + } | ||
| 369 | + ] | ||
| 370 | + }); | ||
| 371 | + }, | ||
| 372 | + // 加载所有需要的字典数据 | ||
| 373 | + // async loadAllDicData() { | ||
| 374 | + // try { | ||
| 375 | + // // 批量加载所有字典数据 | ||
| 376 | + // const dicCodes = ['ENTERPRISE_TYPE']; | ||
| 377 | + // const results = await getDicByCodes(dicCodes); | ||
| 378 | + // // 更新字典选项 | ||
| 379 | + // this.dicOptions.ENTERPRISE_TYPE = results.ENTERPRISE_TYPE.data || []; | ||
| 380 | + // } catch (error) { | ||
| 381 | + // console.error('加载字典数据失败:', error); | ||
| 382 | + // } | ||
| 383 | + // }, | ||
| 384 | + filterStatus(status) { | ||
| 385 | + return this.statusOptions.filter(item => item.value === status)[0].text || ''; | ||
| 386 | + } | ||
| 387 | + } | ||
| 388 | +} | ||
| 389 | +</script> | ||
| 390 | + | ||
| 391 | +<style lang="scss" scoped> | ||
| 392 | +.page { | ||
| 393 | + display: flex; | ||
| 394 | + flex-direction: column; | ||
| 395 | + height: 100vh; | ||
| 396 | +} | ||
| 397 | +.dev-list-fixed { | ||
| 398 | + position: fixed; | ||
| 399 | + top: 96rpx; | ||
| 400 | + left: 0; | ||
| 401 | + right: 0; | ||
| 402 | + z-index: 2; | ||
| 403 | + background: #fff; | ||
| 404 | + | ||
| 405 | + .search-row { | ||
| 406 | + display: flex; | ||
| 407 | + align-items: center; | ||
| 408 | + padding: 16rpx 32rpx; | ||
| 409 | + .uni-searchbar { | ||
| 410 | + padding: 0; | ||
| 411 | + flex: 1; | ||
| 412 | + } | ||
| 413 | + .tool-icons { | ||
| 414 | + display: flex; | ||
| 415 | + .tool-icon { | ||
| 416 | + width: 48rpx; height: 48rpx; display: block; | ||
| 417 | + margin-left: 32rpx; | ||
| 418 | + } | ||
| 419 | + } | ||
| 420 | + } | ||
| 421 | + | ||
| 422 | + .tabs { | ||
| 423 | + display: flex; | ||
| 424 | + align-items: center; | ||
| 425 | + justify-content: space-between; | ||
| 426 | + padding: 26rpx 0; | ||
| 427 | + border-bottom: solid 1px #E7E7E7; | ||
| 428 | + .tab { | ||
| 429 | + width: 180rpx; | ||
| 430 | + text-align: center; | ||
| 431 | + color: #666; | ||
| 432 | + color: rgba(0,0,0,0.9); | ||
| 433 | + line-height: 44rpx; | ||
| 434 | + position: relative; | ||
| 435 | + } | ||
| 436 | + .tab.active { color: $theme-primary; font-weight: 600; } | ||
| 437 | + .tab.active::after { | ||
| 438 | + content: ''; | ||
| 439 | + position: absolute; | ||
| 440 | + left: 50%; | ||
| 441 | + transform: translateX(-50%); | ||
| 442 | + bottom: -13px; | ||
| 443 | + width: 32rpx; | ||
| 444 | + height: 6rpx; | ||
| 445 | + border-radius: 4rpx; | ||
| 446 | + background: $theme-primary; | ||
| 447 | + } | ||
| 448 | + } | ||
| 449 | + .sub-tabs { | ||
| 450 | + display: flex; | ||
| 451 | + align-items: center; | ||
| 452 | + height: 96rpx; | ||
| 453 | + &.bg1 { | ||
| 454 | + background-image: url('/static/images/dev_manage/tab_1_icon.png'); | ||
| 455 | + background-repeat: no-repeat; | ||
| 456 | + background-position: right center; | ||
| 457 | + background-size: cover; | ||
| 458 | + } | ||
| 459 | + &.bg2 { | ||
| 460 | + background-image: url('/static/images/dev_manage/tab_2_icon.png'); | ||
| 461 | + background-repeat: no-repeat; | ||
| 462 | + background-position: right center; | ||
| 463 | + background-size: cover; | ||
| 464 | + } | ||
| 465 | + &.bg3 { | ||
| 466 | + background-image: url('/static/images/dev_manage/tab_3_icon.png'); | ||
| 467 | + background-repeat: no-repeat; | ||
| 468 | + background-position: right center; | ||
| 469 | + background-size: cover; | ||
| 470 | + } | ||
| 471 | + .tab { | ||
| 472 | + width: 50%; | ||
| 473 | + height: 96rpx; | ||
| 474 | + line-height: 96rpx; | ||
| 475 | + text-align: center; | ||
| 476 | + font-weight: 600; | ||
| 477 | + color: rgba(0,0,0,0.9); | ||
| 478 | + &.active { | ||
| 479 | + color: $theme-primary; | ||
| 480 | + } | ||
| 481 | + } | ||
| 482 | + } | ||
| 483 | +} | ||
| 484 | +/* 仅当前页覆盖 uni-search-bar 盒子高度 */ | ||
| 485 | +::v-deep .uni-searchbar__box { | ||
| 486 | + height: 80rpx !important; | ||
| 487 | + justify-content: start; | ||
| 488 | + .uni-searchbar__box-search-input { | ||
| 489 | + font-size: 32rpx !important; | ||
| 490 | + } | ||
| 491 | +} | ||
| 492 | + | ||
| 493 | +.list-box { | ||
| 494 | + flex: 1; | ||
| 495 | + padding-top: 314rpx; | ||
| 496 | + &.pad-batch { padding-bottom: 144rpx; } | ||
| 497 | + .card { position: relative; } | ||
| 498 | + .card-header { | ||
| 499 | + margin-bottom: 28rpx; | ||
| 500 | + position: relative; | ||
| 501 | + | ||
| 502 | + .title { | ||
| 503 | + font-size: 36rpx; | ||
| 504 | + font-weight: 600; | ||
| 505 | + line-height: 50rpx; | ||
| 506 | + color: #323241; | ||
| 507 | + width: 578rpx; | ||
| 508 | + } | ||
| 509 | + | ||
| 510 | + .status { | ||
| 511 | + font-size: 30rpx; | ||
| 512 | + font-weight: 600; | ||
| 513 | + position: absolute; | ||
| 514 | + top: -36rpx; | ||
| 515 | + right: -32rpx; | ||
| 516 | + height: 48rpx; | ||
| 517 | + line-height: 48rpx; | ||
| 518 | + color: #fff; | ||
| 519 | + font-size: 24rpx; | ||
| 520 | + padding: 0 14rpx; | ||
| 521 | + border-radius: 6rpx; | ||
| 522 | + &.status_1 { | ||
| 523 | + background: #3D48A3; | ||
| 524 | + } | ||
| 525 | + &.status_2 { | ||
| 526 | + background: #2BA471; | ||
| 527 | + } | ||
| 528 | + &.status_3 { | ||
| 529 | + background: #D54941; | ||
| 530 | + } | ||
| 531 | + &.status_4 { | ||
| 532 | + background: #E7E7E7; | ||
| 533 | + color: #323241; | ||
| 534 | + } | ||
| 535 | + } | ||
| 536 | + } | ||
| 537 | + .info-row { | ||
| 538 | + display: flex; | ||
| 539 | + align-items: center; | ||
| 540 | + color: rgba(0,0,0,0.6); | ||
| 541 | + font-size: 28rpx; | ||
| 542 | + margin-bottom: 24rpx; | ||
| 543 | + height: 32rpx; | ||
| 544 | + &:last-child { | ||
| 545 | + margin-bottom: 0; | ||
| 546 | + } | ||
| 547 | + text { | ||
| 548 | + width: 60%; | ||
| 549 | + &:last-child { | ||
| 550 | + color: rgba(0,0,0,0.9); | ||
| 551 | + width: 40%; | ||
| 552 | + } | ||
| 553 | + } | ||
| 554 | + } | ||
| 555 | +} | ||
| 556 | + | ||
| 557 | +.batch-bar { | ||
| 558 | + position: fixed; | ||
| 559 | + left: 0; | ||
| 560 | + right: 0; | ||
| 561 | + bottom: 0; | ||
| 562 | + display: flex; | ||
| 563 | + gap: 36rpx; | ||
| 564 | + padding: 32rpx 30rpx; | ||
| 565 | + background: #fff; | ||
| 566 | + box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.04); | ||
| 567 | + .bbtn { | ||
| 568 | + flex: 1; | ||
| 569 | + height: 80rpx; | ||
| 570 | + line-height: 80rpx; | ||
| 571 | + border-radius: 12rpx; | ||
| 572 | + font-size: 32rpx; | ||
| 573 | + display: flex; | ||
| 574 | + align-items: center; | ||
| 575 | + justify-content: center; | ||
| 576 | + } | ||
| 577 | + .reject { | ||
| 578 | + background: #fff; | ||
| 579 | + border: 1rpx solid $uni-color-error; | ||
| 580 | + color: $uni-color-error; | ||
| 581 | + } | ||
| 582 | +} | ||
| 583 | + | ||
| 584 | +.filter-form { | ||
| 585 | + .form-item { margin-bottom: 24rpx; } | ||
| 586 | + .label { | ||
| 587 | + margin-bottom: 20rpx; | ||
| 588 | + color: rgba(0,0,0,0.9); | ||
| 589 | + height: 44rpx; | ||
| 590 | + line-height: 44rpx; | ||
| 591 | + font-size: 30rpx; | ||
| 592 | + } | ||
| 593 | + .fake-select { | ||
| 594 | + height: 80rpx; line-height: 80rpx; padding: 0 20rpx; background: #f3f3f3; border-radius: 12rpx; | ||
| 595 | + .placeholder { color: #999; } | ||
| 596 | + .value { color: #333; } | ||
| 597 | + } | ||
| 598 | +} | ||
| 599 | + | ||
| 600 | +/* 深度覆盖 uni-data-checkbox(mode=tag)内部的 tag 展示与间距 */ | ||
| 601 | +::v-deep .filter-form .uni-data-checklist .checklist-group { | ||
| 602 | + .checklist-box { | ||
| 603 | + &.is--tag { | ||
| 604 | + width: 212rpx; | ||
| 605 | + margin-top: 0; | ||
| 606 | + margin-bottom: 24rpx; | ||
| 607 | + margin-right: 24rpx; | ||
| 608 | + height: 80rpx; | ||
| 609 | + padding: 0; | ||
| 610 | + border-radius: 12rpx; | ||
| 611 | + background-color: #f3f3f3; | ||
| 612 | + border-color: #f3f3f3; | ||
| 613 | + &:nth-child(3n) { | ||
| 614 | + margin-right: 0; | ||
| 615 | + } | ||
| 616 | + .checklist-content { | ||
| 617 | + display: flex; | ||
| 618 | + justify-content: center; | ||
| 619 | + } | ||
| 620 | + .checklist-text { | ||
| 621 | + color: rgba(0,0,0,0.9); | ||
| 622 | + font-size: 28rpx; | ||
| 623 | + } | ||
| 624 | + } | ||
| 625 | + &.is-checked { | ||
| 626 | + background-color: $theme-primary-plain-bg !important; | ||
| 627 | + border-color: $theme-primary-plain-bg !important; | ||
| 628 | + .checklist-text { | ||
| 629 | + color: $theme-primary !important; | ||
| 630 | + } | ||
| 631 | + } | ||
| 632 | + } | ||
| 633 | + | ||
| 634 | +} | ||
| 635 | + | ||
| 636 | +</style> |
static/images/dev_manage/add_icon.png
0 → 100644
349 Bytes
static/images/dev_manage/batch_icon.png
0 → 100644
1018 Bytes
static/images/dev_manage/close_icon.png
0 → 100644
835 Bytes
static/images/dev_manage/filter_icon.png
0 → 100644
669 Bytes
static/images/dev_manage/tab_1_icon.png
0 → 100644
2.17 KB
static/images/dev_manage/tab_2_icon.png
0 → 100644
2.46 KB
static/images/dev_manage/tab_3_icon.png
0 → 100644
2.16 KB
static/images/empty/empty_bg.png
0 → 100644
63 KB
| @@ -15,14 +15,37 @@ | @@ -15,14 +15,37 @@ | ||
| 15 | 15 | ||
| 16 | /* 页面原生导航标题字号:H5 覆盖为 18px */ | 16 | /* 页面原生导航标题字号:H5 覆盖为 18px */ |
| 17 | /* #ifdef H5 */ | 17 | /* #ifdef H5 */ |
| 18 | +.uni-page-head { | ||
| 19 | + height: 96rpx !important; | ||
| 20 | +} | ||
| 18 | .uni-page-head .uni-page-head-title { | 21 | .uni-page-head .uni-page-head-title { |
| 19 | - font-size: 18px !important; | 22 | + font-size: 36rpx !important; |
| 23 | + line-height: 96rpx !important; | ||
| 24 | + color: rgba(0,0,0,0.9) !important; | ||
| 25 | +} | ||
| 26 | +.uni-page-head .uni-page-head-btn .uni-icons, | ||
| 27 | +.uni-page-head .uni-page-head-btn .uniui-back { | ||
| 28 | + color: rgba(0,0,0,0.9) !important; | ||
| 20 | } | 29 | } |
| 21 | /* #endif */ | 30 | /* #endif */ |
| 22 | 31 | ||
| 23 | /* 组件 uni-nav-bar 标题字号统一为 18px */ | 32 | /* 组件 uni-nav-bar 标题字号统一为 18px */ |
| 24 | .uni-nav-bar-text { | 33 | .uni-nav-bar-text { |
| 25 | - font-size: 18px !important; | 34 | + font-size: 36rpx !important; |
| 35 | +} | ||
| 36 | + | ||
| 37 | +/* 全局导航栏背景采用主题色(含内容容器与头部),并统一文字/图标为白色 */ | ||
| 38 | +.uni-navbar__content, | ||
| 39 | +.uni-navbar__header { | ||
| 40 | + background-color: $theme-primary !important; | ||
| 41 | +} | ||
| 42 | +.uni-navbar--border { | ||
| 43 | + border-bottom-color: $theme-primary-plain-border !important; | ||
| 44 | +} | ||
| 45 | +.uni-navbar__header .uni-nav-bar-text, | ||
| 46 | +.uni-navbar__header .uni-navbar-btn-text text, | ||
| 47 | +.uni-navbar__header .uni-icons { | ||
| 48 | + color: #ffffff !important; | ||
| 26 | } | 49 | } |
| 27 | 50 | ||
| 28 | /* 全端覆盖 primary 按钮主色(内置 <button type="primary">) */ | 51 | /* 全端覆盖 primary 按钮主色(内置 <button type="primary">) */ |
| @@ -9,7 +9,7 @@ $theme-primary: #3d48a3; | @@ -9,7 +9,7 @@ $theme-primary: #3d48a3; | ||
| 9 | $theme-primary-hover: darken($theme-primary, 6%); | 9 | $theme-primary-hover: darken($theme-primary, 6%); |
| 10 | $theme-primary-active: darken($theme-primary, 10%); | 10 | $theme-primary-active: darken($theme-primary, 10%); |
| 11 | $theme-primary-disabled: mix(#ffffff, $theme-primary, 60%); | 11 | $theme-primary-disabled: mix(#ffffff, $theme-primary, 60%); |
| 12 | -$theme-primary-plain-bg: mix(#ffffff, $theme-primary, 92%); | 12 | +$theme-primary-plain-bg: mix(#ffffff, $theme-primary, 88%); |
| 13 | $theme-primary-plain-border: mix(#ffffff, $theme-primary, 30%); | 13 | $theme-primary-plain-border: mix(#ffffff, $theme-primary, 30%); |
| 14 | // 半透明主题色(0.5 不透明度,用于禁用态或蒙层) | 14 | // 半透明主题色(0.5 不透明度,用于禁用态或蒙层) |
| 15 | $theme-primary-alpha-50: rgba($theme-primary, 0.5); | 15 | $theme-primary-alpha-50: rgba($theme-primary, 0.5); |
utils/dic.js
0 → 100644
| 1 | +import { getDicByCodeApi } from '@/api/base.js'; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 获取单个字典数据(无缓存版本) | ||
| 5 | + * @param {string} code | ||
| 6 | + * @returns {Promise<Array>} | ||
| 7 | + */ | ||
| 8 | +export function getDicByCode(code) { | ||
| 9 | + return getDicByCodeApi(code); | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 批量获取多个字典数据(无缓存版本) | ||
| 14 | + * @param {string[]} codes | ||
| 15 | + * @returns {Promise<Object>} | ||
| 16 | + */ | ||
| 17 | +export function getDicByCodes(codes) { | ||
| 18 | + const list = Array.isArray(codes) ? codes : []; | ||
| 19 | + const promises = list.map(code => getDicByCodeApi(code)); | ||
| 20 | + return Promise.all(promises).then(results => { | ||
| 21 | + const result = {}; | ||
| 22 | + list.forEach((code, index) => { | ||
| 23 | + result[code] = results[index] || []; | ||
| 24 | + }); | ||
| 25 | + return result; | ||
| 26 | + }); | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * 获取字典项的名称(需要传入字典项数组) | ||
| 31 | + * @param {string} code 字典编码(仅用于标识,实际不使用) | ||
| 32 | + * @param {string} value 字典值 | ||
| 33 | + * @param {Array} items 字典项数组 | ||
| 34 | + * @returns {string} | ||
| 35 | + */ | ||
| 36 | +export function getDicName(code, value, items = []) { | ||
| 37 | + const list = Array.isArray(items) ? items : []; | ||
| 38 | + const item = list.find(it => it && it.code === value); | ||
| 39 | + return item ? item.name : value; | ||
| 40 | +} |
utils/httpEnum.js
0 → 100644
| 1 | +/** | ||
| 2 | + * 内容类型枚举(纯 JS 兼容导出) | ||
| 3 | + * 说明:原文件使用了 TypeScript 的 enum 语法,但当前文件为 .js。 | ||
| 4 | + * 为避免在不同构建目标(H5/小程序/APP)下因语法不兼容导致构建或运行问题, | ||
| 5 | + * 改为导出只读对象,保持同名导出与调用方式不变。 | ||
| 6 | + */ | ||
| 7 | +export const ContentTypeEnum = Object.freeze({ | ||
| 8 | + // JSON | ||
| 9 | + JSON: 'application/json;charset=UTF-8', | ||
| 10 | + // 表单(urlencoded) | ||
| 11 | + FORM_URLENCODED: 'application/x-www-form-urlencoded;charset=UTF-8', | ||
| 12 | + // 文件上传(multipart/form-data) | ||
| 13 | + BLOB: 'multipart/form-data;charset=UTF-8' | ||
| 14 | +}) |