From 8b908f5ac97925fde7934f7f28666986cc8091d6 Mon Sep 17 00:00:00 2001 From: Megghy Date: Mon, 28 Apr 2025 04:04:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=AD=8C=E6=9B=B2?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=88=86=E9=A1=B5=E5=8A=9F=E8=83=BD=E5=92=8C?= =?UTF-8?q?=E9=94=AE=E7=9B=98=E5=BF=AB=E6=8D=B7=E9=94=AE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 SongList 组件中实现分页功能,支持上一页和下一页操作 - 添加键盘快捷键,允许用户通过方向键进行翻页 - 优化组件结构,增强可读性和用户体验 --- message_render_content.txt | Bin 0 -> 45752 bytes src/components/SongList.vue | 32 +- src/components/manage/PointOrderCard.vue | 1017 ++++++++++---- src/data/TemplateTypes.ts | 1 + src/data/constants.ts | 24 +- src/views/manage/SettingsManageView.vue | 2 +- src/views/manage/point/PointManage.vue | 1218 ++++++++++------- src/views/manage/point/PointUserManage.vue | 146 +- src/views/obs/blivechat/MessageRender.vue | 4 +- src/views/obs/blivechat/TextMessage.vue | 4 +- src/views/obs/blivechat/constants.js | 24 +- .../indexTemplate/DefaultIndexTemplate.vue | 1 + .../DefaultSongListTemplate.vue | 152 +- 13 files changed, 1712 insertions(+), 913 deletions(-) create mode 100644 message_render_content.txt diff --git a/message_render_content.txt b/message_render_content.txt new file mode 100644 index 0000000000000000000000000000000000000000..58cbbac1e75d4a7be84b24a9ce61cc7aef0e43c8 GIT binary patch literal 45752 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ⅇ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 F`#(p(E7|}6 literal 0 HcmV?d00001 diff --git a/src/components/SongList.vue b/src/components/SongList.vue index 002642c..268df0f 100644 --- a/src/components/SongList.vue +++ b/src/components/SongList.vue @@ -87,6 +87,33 @@ const batchUpdate_Option = ref() // 批量编辑 const columns = ref>() // 表格列定义 const selectedColumn = ref([]) // 表格选中行的 Key 数组 +// 分页相关 +const currentPage = ref(1) // 当前页码 +const handlePageChange = (page: number) => { + currentPage.value = page +} + +// 暴露分页方法 +const nextPage = () => { + const pagination = songsComputed.value.length > 0 ? Math.ceil(songsComputed.value.length / pageSize.value) : 1 + if (currentPage.value < pagination) { + currentPage.value++ + } +} + +const prevPage = () => { + if (currentPage.value > 1) { + currentPage.value-- + } +} + +// 暴露给父组件 +defineExpose({ + nextPage, + prevPage, + currentPage +}) + // --- 计算属性 --- // 筛选后的歌曲列表 @@ -163,8 +190,6 @@ const authorsOptions = computed(() => { })) }) -// --- 表格列定义 --- - // 作者列定义 (包含筛选逻辑) const authorColumn = ref>({ title: '作者', @@ -751,7 +776,8 @@ onMounted(() => { pageSizes: [10, 25, 50, 100, 200], showSizePicker: true, showQuickJumper: true, - + page: currentPage, + onUpdatePage: handlePageChange }" :loading="isLoading && songsComputed.length === 0" striped diff --git a/src/components/manage/PointOrderCard.vue b/src/components/manage/PointOrderCard.vue index b69f017..5720e4f 100644 --- a/src/components/manage/PointOrderCard.vue +++ b/src/components/manage/PointOrderCard.vue @@ -32,58 +32,101 @@ import { NTooltip, useDialog, useMessage, + NCard, + NSpace, + NAlert, } from 'naive-ui' import { computed, h, onMounted, ref, watch } from 'vue' import AddressDisplay from './AddressDisplay.vue' import PointGoodsItem from './PointGoodsItem.vue' +type OrderType = ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel + const props = defineProps<{ order: ResponsePointOrder2UserModel[] | ResponsePointOrder2OwnerModel[] type: 'user' | 'owner' goods?: ResponsePointGoodModel[] loading?: boolean }>() + const message = useMessage() const dialog = useDialog() - -const isLoading = ref(false) -watch( - () => props.loading, - () => { - isLoading.value = props.loading - }, -) -const orderAsUser = computed(() => { - return props.order as ResponsePointOrder2UserModel[] -}) -const orderAsOwner = computed(() => { - return props.order as ResponsePointOrder2OwnerModel[] -}) -const selectedItem = ref() - const emit = defineEmits(['selectedItem']) +// 状态管理 +const isLoading = ref(false) const showDetailModal = ref(false) -const orderDetail = ref() -const currentGoods = computed(() => { - //@ts-ignore - if (props.type == 'user') return orderDetail.value.goods - //@ts-ignore - else return props.goods.find((g) => g.id == orderDetail.value.goodsId) +const selectedItem = ref([]) +const orderDetail = ref() + +// 监听加载状态 +watch(() => props.loading, (val) => { + isLoading.value = !!val }) + +// 计算属性 +const orderAsUser = computed(() => props.order as ResponsePointOrder2UserModel[]) +const orderAsOwner = computed(() => props.order as ResponsePointOrder2OwnerModel[]) + +const currentGoods = computed(() => { + if (!orderDetail.value) return null + + if (props.type === 'user') { + return (orderDetail.value as ResponsePointOrder2UserModel).goods + } else { + return props.goods?.find((g) => g.id === (orderDetail.value as ResponsePointOrder2OwnerModel).goodsId) + } +}) + const expressOptions = computed(() => { - if (!orderAsOwner.value) return [] - return orderAsOwner.value.map((o) => ({ - label: o.expressCompany, - value: o.expressCompany, + if (props.type !== 'owner' || !orderAsOwner.value) return [] + + // 过滤掉空值并去重 + const companies = [...new Set( + orderAsOwner.value + .map(o => o.expressCompany) + .filter(Boolean) + )] + + return companies.map(company => ({ + label: company, + value: company, })) }) -const orderColumn: DataTableColumns = [ +// 状态映射表 +const statusMap = { + [PointOrderStatus.Pending]: { + text: '等待发货', + type: 'default', + description: '订单创建完成,等待主播发货', + action: '发货', + nextStatusText: '确认发货后,订单状态将变为"已发货"', + prevStatusText: '' + }, + [PointOrderStatus.Shipped]: { + text: (hasExpress: boolean) => hasExpress ? '已发货 | 已填写单号' : '已发货 | 未填写单号', + type: (hasExpress: boolean) => hasExpress ? 'info' : 'warning', + description: '订单已发货,可以添加快递信息', + action: '完成订单', + nextStatusText: '确认后将可以进行发货信息填写', + prevStatusText: '回退到"等待发货"状态,适用于发货信息填写错误等情况' + }, + [PointOrderStatus.Completed]: { + text: '已完成', + type: 'success', + description: '订单已完成', + action: '', + nextStatusText: '完成后无法再进行状态修改', + prevStatusText: '回退到"已发货"状态(仅限实体礼物)' + }, +} + +// 表格列定义 +const orderColumn: DataTableColumns = [ { type: 'selection', - - disabled: () => props.type == 'user', + disabled: () => props.type === 'user', options: [ 'all', 'none', @@ -91,8 +134,9 @@ const orderColumn: DataTableColumns { - selectedItem.value = pageData.filter((row) => row.status == PointOrderStatus.Pending).map((row) => row.id) - console.log(selectedItem.value) + selectedItem.value = pageData + .filter((row) => row.status === PointOrderStatus.Pending) + .map((row) => row.id) }, }, ], @@ -104,32 +148,36 @@ const orderColumn: DataTableColumns props.type == 'user', - render: (row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => { - return row.instanceOf == 'user' - ? '' - : h(NTooltip, null, { - trigger: () => - h( - NButton, - { - text: true, - type: 'primary', - tag: 'a', - href: 'https://space.bilibili.com/' + row.customer?.userId + '', - target: '_blank', - }, - { default: () => row.customer?.name || '未知用户' }, - ), - default: () => row.customer?.userId || '未知ID', - }) + disabled: () => props.type === 'user', + render: (row: OrderType) => { + if (row.instanceOf === 'user') return '' + + const ownerRow = row as ResponsePointOrder2OwnerModel + return h(NTooltip, null, { + trigger: () => + h( + NButton, + { + text: true, + type: 'primary', + tag: 'a', + href: `https://space.bilibili.com/${ownerRow.customer?.userId || ''}`, + target: '_blank', + }, + { default: () => ownerRow.customer?.name || '未知用户' }, + ), + default: () => ownerRow.customer?.userId || '未知ID', + }) }, }, { title: '礼物名', key: 'giftName', - render: (row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => { - return row.instanceOf == 'user' ? row.goods.name : props.goods?.find((g) => g.id == row.goodsId)?.name + render: (row: OrderType) => { + if (row.instanceOf === 'user') { + return (row as ResponsePointOrder2UserModel).goods.name + } + return props.goods?.find((g) => g.id === row.goodsId)?.name || '未知礼物' }, }, { @@ -141,7 +189,7 @@ const orderColumn: DataTableColumns { + render: (row: OrderType) => { return h(NTooltip, null, { trigger: () => h(NTime, { time: row.createAt, type: 'relative' }), default: () => h(NTime, { time: row.createAt }), @@ -156,88 +204,64 @@ const orderColumn: DataTableColumns { - return row.status == filterOptionValue - }, + filter: props.type === 'owner' + ? undefined + : (filterOptionValue: unknown, row: OrderType) => row.status === filterOptionValue, filterOptions: [ - { - label: '等待发货', - value: PointOrderStatus.Pending, - }, - { - label: '已发货', - value: PointOrderStatus.Shipped, - }, - { - label: '已完成', - value: PointOrderStatus.Completed, - }, + { label: '等待发货', value: PointOrderStatus.Pending }, + { label: '已发货', value: PointOrderStatus.Shipped }, + { label: '已完成', value: PointOrderStatus.Completed }, ], - render: (row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => { - const statusMap = { - [PointOrderStatus.Pending]: { - text: '等待发货', - type: 'default' - }, - [PointOrderStatus.Shipped]: { - text: row.expressCompany ? '已发货 | 已填写单号' : '已发货 | 未填写单号', - type: row.expressCompany ? 'info' : 'warning' - }, - [PointOrderStatus.Completed]: { - text: '已完成', - type: 'success' - } - } - + render: (row: OrderType) => { const status = statusMap[row.status] || { text: '未知状态', type: 'error' } + const hasExpress = !!row.expressCompany + + const text = typeof status.text === 'function' ? status.text(hasExpress) : status.text + const type = typeof status.type === 'function' ? status.type(hasExpress) : status.type return h(NTag, { size: 'small', - type: status.type as any, + type: type as any, bordered: false - }, () => status.text) + }, () => text) }, }, { title: '订单类型', key: 'type', - filter: - props.type == 'owner' - ? undefined - : (filterOptionValue: unknown, row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => { - return row.type == filterOptionValue - }, + filter: props.type === 'owner' + ? undefined + : (filterOptionValue: unknown, row: OrderType) => row.type === filterOptionValue, filterOptions: [ - { - label: '实体礼物', - value: GoodsTypes.Physical, - }, - { - label: '虚拟礼物', - value: GoodsTypes.Virtual, - }, + { label: '实体礼物', value: GoodsTypes.Physical }, + { label: '虚拟礼物', value: GoodsTypes.Virtual }, ], - render: (row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => { - return h(NTag, { type: 'success', bordered: false, size: 'small' }, () => - row.type == GoodsTypes.Physical ? '实体礼物' : '虚拟礼物', - ) + render: (row: OrderType) => { + return h(NTag, { + type: 'success', + bordered: false, + size: 'small' + }, () => row.type === GoodsTypes.Physical ? '实体礼物' : '虚拟礼物') }, }, { title: '地址', key: 'address', minWidth: 250, - render: (row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => { - const collectUrl = - row.instanceOf == 'user' ? row.goods.collectUrl : props.goods?.find((g) => g.id == row.goodsId)?.collectUrl - if (row.type == GoodsTypes.Physical) { - return collectUrl - ? h(NButton, { tag: 'a', href: collectUrl, target: '_blank', text: true, type: 'info' }, () => - h(NText, { italic: true }, () => '通过站外链接收集'), - ) + render: (row: OrderType) => { + const goodsCollectUrl = row.instanceOf === 'user' + ? (row as ResponsePointOrder2UserModel).goods.collectUrl + : props.goods?.find((g) => g.id === row.goodsId)?.collectUrl + + if (row.type === GoodsTypes.Physical) { + return goodsCollectUrl + ? h(NButton, { + tag: 'a', + href: goodsCollectUrl, + target: '_blank', + text: true, + type: 'info' + }, () => h(NText, { italic: true }, () => '通过站外链接收集')) : h(AddressDisplay, { address: row.address }) } else { return h(NText, { depth: 3, italic: true }, () => '无需发货') @@ -248,36 +272,23 @@ const orderColumn: DataTableColumns { - if (row.type == GoodsTypes.Physical) { - return row.trackingNumber - ? h( - NFlex, - { - depth: 3, - }, - () => [ - h( - NTag, - { - size: 'tiny', - bordered: false, - }, - () => row.expressCompany, - ), - h(NText, { depth: 3 }, () => row.trackingNumber), - ], - ) - : h(NText, { depth: 3, italic: true }, () => '尚未发货') - } else { - return h(NText, { depth: 3, italic: true }, () => '无需发货') + render: (row: OrderType) => { + if (row.type === GoodsTypes.Physical) { + if (row.trackingNumber) { + return h(NFlex, { align: 'center', gap: 8 }, () => [ + h(NTag, { size: 'tiny', bordered: false }, () => row.expressCompany), + h(NText, { depth: 3 }, () => row.trackingNumber), + ]) + } + return h(NText, { depth: 3, italic: true }, () => '尚未发货') } + return h(NText, { depth: 3, italic: true }, () => '无需发货') }, }, { title: '操作', key: 'action', - render: (row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => { + render: (row: OrderType) => { return h( NButton, { @@ -293,10 +304,65 @@ const orderColumn: DataTableColumns (orderDetail.value?.status || 0)) { + tipText = statusInfo.nextStatusText + + // 特殊处理虚拟礼物 + if (orderDetail.value?.type === GoodsTypes.Virtual && status === PointOrderStatus.Completed) { + tipText = '该虚拟礼物将被标记为已完成' + } + } else if (status < (orderDetail.value?.status || 0)) { + tipText = statusInfo.prevStatusText + } + dialog.info({ - title: '提示', - content: '确认修改订单状态?', + title: '修改订单状态', + content: () => h('div', null, [ + h('p', null, `确认将订单状态从「${currentStatusText}」修改为「${newStatusText}」吗?`), + tipText ? h('p', { style: 'color: #f90; margin-top: 8px;' }, tipText) : null + ]), positiveText: '确认', negativeText: '取消', onPositiveClick: () => { @@ -304,17 +370,21 @@ function onChangeStatus(id: number, status: PointOrderStatus) { }, }) } -async function updateStatus(id: number[], status: PointOrderStatus) { + +async function updateStatus(ids: number[], status: PointOrderStatus) { + if (!ids.length) return + try { isLoading.value = true const data = await QueryPostAPI(POINT_API_URL + 'update-orders-status', { - ids: id, + ids, status, }) - if (data.code == 200) { + + if (data.code === 200) { message.success('操作成功') props.order?.forEach((row) => { - if (id.includes(row.id)) { + if (ids.includes(row.id)) { row.status = status } }) @@ -323,16 +393,18 @@ async function updateStatus(id: number[], status: PointOrderStatus) { } } catch (err) { message.error('操作失败: ' + err) - console.log(err) + console.error(err) } finally { isLoading.value = false } } + async function updateExpress(item: ResponsePointOrder2OwnerModel) { if (!item.trackingNumber || !item.expressCompany) { message.error('请填写快递单号和快递公司') return } + try { isLoading.value = true const data = await QueryPostAPI(POINT_API_URL + 'update-order-express', { @@ -340,18 +412,21 @@ async function updateExpress(item: ResponsePointOrder2OwnerModel) { trackingNumber: item.trackingNumber, expressCompany: item.expressCompany, }) - if (data.code == 200) { + + if (data.code === 200) { message.success('操作成功') } else { message.error('操作失败: ' + data.message) } } catch (err) { message.error('操作失败: ' + err) - console.log(err) + console.error(err) + } finally { + isLoading.value = false } - isLoading.value = false } +// 初始化 onMounted(() => { props.order?.forEach((row) => { row.instanceOf = props.type @@ -360,151 +435,521 @@ onMounted(() => {