Compare commits
3 Commits
49dbde11e0
...
202f4bbd44
| Author | SHA1 | Date | |
|---|---|---|---|
| 202f4bbd44 | |||
| 3d6aa6026c | |||
| 28e339a048 |
@@ -88,3 +88,40 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 二次确认弹窗 =====
|
||||
|
||||
.confirmContent {
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
|
||||
strong {
|
||||
margin: 0 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.confirmTip {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tabularLink {
|
||||
font-variant-numeric: tabular-nums;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:global(.toggleConfirmModal) {
|
||||
.ant-modal-confirm-body {
|
||||
padding: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.deleteConfirmModal) {
|
||||
.ant-modal-confirm-body {
|
||||
padding: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
.statusTag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 1px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid transparent;
|
||||
letter-spacing: 0.2px;
|
||||
|
||||
&.dimmed {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #1677ff;
|
||||
background: rgba(22, 119, 255, 0.08);
|
||||
border-color: rgba(22, 119, 255, 0.18);
|
||||
|
||||
.dot {
|
||||
background: #1677ff;
|
||||
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #389e0d;
|
||||
background: rgba(82, 196, 26, 0.1);
|
||||
border-color: rgba(82, 196, 26, 0.22);
|
||||
|
||||
.dot {
|
||||
background: #52c41a;
|
||||
box-shadow: 0 0 0 2px rgba(82, 196, 26, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: #d48806;
|
||||
background: rgba(250, 173, 20, 0.12);
|
||||
border-color: rgba(250, 173, 20, 0.28);
|
||||
|
||||
.dot {
|
||||
background: #faad14;
|
||||
box-shadow: 0 0 0 2px rgba(250, 173, 20, 0.18);
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: #cf1322;
|
||||
background: rgba(255, 77, 79, 0.08);
|
||||
border-color: rgba(255, 77, 79, 0.2);
|
||||
|
||||
.dot {
|
||||
background: #ff4d4f;
|
||||
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.orange {
|
||||
color: #d46b08;
|
||||
background: rgba(250, 140, 22, 0.1);
|
||||
border-color: rgba(250, 140, 22, 0.22);
|
||||
|
||||
.dot {
|
||||
background: #fa8c16;
|
||||
box-shadow: 0 0 0 2px rgba(250, 140, 22, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.cyan {
|
||||
color: #08979c;
|
||||
background: rgba(19, 194, 194, 0.1);
|
||||
border-color: rgba(19, 194, 194, 0.22);
|
||||
|
||||
.dot {
|
||||
background: #13c2c2;
|
||||
box-shadow: 0 0 0 2px rgba(19, 194, 194, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.purple {
|
||||
color: #531dab;
|
||||
background: rgba(114, 46, 209, 0.08);
|
||||
border-color: rgba(114, 46, 209, 0.2);
|
||||
|
||||
.dot {
|
||||
background: #722ed1;
|
||||
box-shadow: 0 0 0 2px rgba(114, 46, 209, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.neutral {
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
border-color: rgba(0, 0, 0, 0.08);
|
||||
|
||||
.dot {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.default {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
background: #fafafa;
|
||||
border-color: #f0f0f0;
|
||||
|
||||
.dot {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { defineComponent, type PropType } from 'vue';
|
||||
import styles from './StatusTag.module.less';
|
||||
|
||||
export type StatusTagTone =
|
||||
| 'primary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| 'danger'
|
||||
| 'orange'
|
||||
| 'cyan'
|
||||
| 'purple'
|
||||
| 'neutral'
|
||||
| 'default';
|
||||
|
||||
const StatusTag = defineComponent({
|
||||
name: 'StatusTag',
|
||||
props: {
|
||||
label: { type: String, required: true },
|
||||
tone: { type: String as PropType<StatusTagTone>, default: 'default' },
|
||||
showDot: { type: Boolean, default: true },
|
||||
dimmed: { type: Boolean, default: false },
|
||||
},
|
||||
setup(props) {
|
||||
return () => (
|
||||
<span
|
||||
class={[styles.statusTag, styles[props.tone], props.dimmed && styles.dimmed]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{props.showDot ? <span class={styles.dot} /> : null}
|
||||
<span>{props.label}</span>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default StatusTag;
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as StatusTag } from './StatusTag';
|
||||
export type { StatusTagTone } from './StatusTag';
|
||||
@@ -4,4 +4,6 @@
|
||||
*/
|
||||
export { default as HelloWorld } from './HelloWorld';
|
||||
export { default as ChangePasswordModal } from './ChangePasswordModal';
|
||||
export { default as PageTabs } from './pageTabs/PageTabs';
|
||||
export { default as PageTabs } from './PageTabs/PageTabs';
|
||||
export { StatusTag } from './StatusTag';
|
||||
export type { StatusTagTone } from './StatusTag';
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
}
|
||||
|
||||
.uploadIcon {
|
||||
font-size: 32px;
|
||||
font-size: 24px;
|
||||
color: #999;
|
||||
font-weight: 300;
|
||||
line-height: 1;
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from 'ant-design-vue';
|
||||
import { JUMP_TYPE_OPTIONS, EVENT_OPTIONS, REGION_OPTIONS } from '../model/useBannerModel';
|
||||
import { ImageCropper } from '@yp-component/root';
|
||||
import { FileImageOutlined } from '@ant-design/icons-vue';
|
||||
import { uploadFile, OssUploadType } from '@/utils/oss';
|
||||
import dayjs from 'dayjs';
|
||||
import styles from './BannerFormModal.module.less';
|
||||
@@ -317,7 +318,9 @@ export default defineComponent({
|
||||
}`}
|
||||
onClick={triggerFilePicker}
|
||||
>
|
||||
<span class={styles.uploadIcon}>+</span>
|
||||
<span class={styles.uploadIcon}>
|
||||
<FileImageOutlined />
|
||||
</span>
|
||||
<span class={styles.uploadHint}>点击上传</span>
|
||||
<span class={styles.uploadTip}>
|
||||
建议尺寸 1053*351 像素
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
.eventBannerMain {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tableBody {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
:global {
|
||||
.ant-spin-nested-loading,
|
||||
.ant-spin-container,
|
||||
.ant-table,
|
||||
.ant-table-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
import { useBannerModel, BANNER_STATUS_OPTIONS } from './model/useBannerModel';
|
||||
import { useContainerSize } from '@/hooks';
|
||||
import BannerFormModal from './components/BannerFormModal';
|
||||
import styles from './index.module.less';
|
||||
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||
|
||||
/**
|
||||
@@ -66,7 +65,7 @@ function renderBodyCell({
|
||||
);
|
||||
}
|
||||
|
||||
// 跳转类型列:彩色 Tag
|
||||
// 跳转类型列:StatusTag
|
||||
if (column.dataIndex === 'jumpType') {
|
||||
return renderJumpType(text);
|
||||
}
|
||||
@@ -231,8 +230,7 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 表格区 ===== */}
|
||||
<div class={pageStyles.table}>
|
||||
<div class={styles.eventBannerMain}>
|
||||
<div ref={containerRef} class={styles.tableBody}>
|
||||
<div ref={containerRef} class={pageStyles.tableBody}>
|
||||
<Table
|
||||
columns={tableColumns}
|
||||
dataSource={dataSource.value}
|
||||
@@ -267,7 +265,6 @@ export default defineComponent({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ===== 新增/编辑弹窗 ===== */}
|
||||
{modalVisible.value && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
|
||||
// ============================================================
|
||||
@@ -21,18 +22,18 @@ export const JUMP_TYPE_OPTIONS = [
|
||||
{ value: 'none', label: '无链接' },
|
||||
] as const;
|
||||
|
||||
/** 跳转类型映射(用于表格 Tag 渲染) */
|
||||
const JUMP_TYPE_MAP: Record<string, { label: string; color: string }> = {
|
||||
event_detail: { label: '赛事详情页', color: 'blue' },
|
||||
miniprogram_page: { label: '小程序页面', color: 'cyan' },
|
||||
custom_link: { label: '自定义链接', color: 'purple' },
|
||||
none: { label: '无链接', color: 'default' },
|
||||
/** 跳转类型映射(用于表格 StatusTag 渲染) */
|
||||
const JUMP_TYPE_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
event_detail: { label: '赛事详情页', tone: 'primary' },
|
||||
miniprogram_page: { label: '小程序页面', tone: 'cyan' },
|
||||
custom_link: { label: '自定义链接', tone: 'purple' },
|
||||
none: { label: '无链接', tone: 'default' },
|
||||
};
|
||||
|
||||
/** Banner 状态映射 */
|
||||
const STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
enabled: { label: '启用中', color: 'green' },
|
||||
disabled: { label: '已禁用', color: 'default' },
|
||||
const STATUS_MAP: Record<string, { label: string; tone: StatusTagTone; dimmed?: boolean }> = {
|
||||
enabled: { label: '启用中', tone: 'success' },
|
||||
disabled: { label: '已禁用', tone: 'default', dimmed: true },
|
||||
};
|
||||
|
||||
/** 关联赛事选项(mock) */
|
||||
@@ -153,7 +154,6 @@ export function useBannerModel() {
|
||||
dataIndex: 'jumpType',
|
||||
key: 'jumpType',
|
||||
width: 120,
|
||||
align: 'center' as const,
|
||||
},
|
||||
{ title: '关联赛事', dataIndex: 'relatedEvent', key: 'relatedEvent', width: 200 },
|
||||
{ title: '所属区域', key: 'region', width: 140 },
|
||||
@@ -282,18 +282,17 @@ export function useBannerModel() {
|
||||
|
||||
// ===== 供页面 bodyCell 使用的渲染工具 =====
|
||||
const renderJumpType = (type: string) => {
|
||||
const info = JUMP_TYPE_MAP[type] || { label: type || '-', color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = JUMP_TYPE_MAP[type] || { label: type || '-', tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
};
|
||||
|
||||
const renderStatus = (status: string) => {
|
||||
const info = STATUS_MAP[status] || { label: status || '-', color: 'default' };
|
||||
const tagProps: any = { color: info.color };
|
||||
// 已禁用状态添加半透明样式
|
||||
if (status === 'disabled') {
|
||||
tagProps.style = { opacity: 0.5 };
|
||||
}
|
||||
return h(Tag, tagProps, () => info.label);
|
||||
const info = STATUS_MAP[status] || { label: status || '-', tone: 'default' as const };
|
||||
return h(StatusTag, {
|
||||
label: info.label,
|
||||
tone: info.tone,
|
||||
dimmed: info.dimmed,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
.eventListMain {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tableBody {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
:global {
|
||||
.ant-spin-nested-loading,
|
||||
.ant-spin-container,
|
||||
.ant-table,
|
||||
.ant-table-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import { useContainerSize, useState } from '@/hooks';
|
||||
import EventRegulationModal from './components/EventRegulationModal';
|
||||
import EventDetailModal from './components/EventDetailModal';
|
||||
import EventAuditModal from './components/EventAuditModal';
|
||||
import styles from './index.module.less';
|
||||
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
@@ -245,7 +244,7 @@ export default defineComponent({
|
||||
<Select
|
||||
value={filterForm.status}
|
||||
options={EVENT_STATUS_OPTIONS as any}
|
||||
style={{ width: '110px' }}
|
||||
style={{ width: '150px' }}
|
||||
allowClear
|
||||
onUpdate:value={(val: any) => (filterForm.status = val || '')}
|
||||
/>
|
||||
@@ -301,8 +300,7 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 表格区 ===== */}
|
||||
<div class={pageStyles.table}>
|
||||
<div class={styles.eventListMain}>
|
||||
<div ref={containerRef} class={styles.tableBody}>
|
||||
<div ref={containerRef} class={pageStyles.tableBody}>
|
||||
<Table
|
||||
columns={tableColumns}
|
||||
dataSource={dataSource.value}
|
||||
@@ -335,7 +333,6 @@ export default defineComponent({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ===== 弹窗区 ===== */}
|
||||
{regulationModalVisible.value && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
|
||||
// ============================================================
|
||||
@@ -67,12 +68,12 @@ const MOCK_DATA = Array.from({ length: 12 }, (_, i) => ({
|
||||
}));
|
||||
|
||||
/** 状态标签映射 */
|
||||
const STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
pending: { label: '待审核', color: 'gold' },
|
||||
enrolling: { label: '报名中', color: 'blue' },
|
||||
deleted: { label: '已删除', color: 'red' },
|
||||
removed: { label: '已下架', color: 'orange' },
|
||||
rejected: { label: '审核不通过', color: 'volcano' },
|
||||
const STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
pending: { label: '待审核', tone: 'warning' },
|
||||
enrolling: { label: '报名中', tone: 'primary' },
|
||||
deleted: { label: '已删除', tone: 'neutral' },
|
||||
removed: { label: '已下架', tone: 'orange' },
|
||||
rejected: { label: '审核不通过', tone: 'danger' },
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
@@ -169,9 +170,10 @@ export function useEventListModel() {
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 90,
|
||||
align: 'center' as const,
|
||||
customRender: ({ text }: { text: string }) => {
|
||||
const info = STATUS_MAP[text] || { label: text, color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = STATUS_MAP[text] || { label: text, tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
},
|
||||
},
|
||||
{ title: '报名人数', dataIndex: 'enrolledCount', key: 'enrolledCount', width: 90 },
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { defineComponent, ref, computed } from 'vue';
|
||||
import { Modal, Table, Button, Tag, Pagination, Image, Space } from 'ant-design-vue';
|
||||
import { Modal, Table, Button, Pagination, Image, Space } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import styles from './OrderDetailModal.module.less';
|
||||
|
||||
interface OrderDetailModalProps {
|
||||
@@ -69,9 +70,9 @@ const PLAYER_COLUMNS = [
|
||||
},
|
||||
];
|
||||
|
||||
const REFUND_STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
success: { label: '退款成功', color: 'green' },
|
||||
failed: { label: '退款失败', color: 'red' },
|
||||
const REFUND_STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
success: { label: '退款成功', tone: 'success' },
|
||||
failed: { label: '退款失败', tone: 'danger' },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
@@ -203,14 +204,14 @@ export default defineComponent({
|
||||
{pagedRefunds.value.map((r, idx) => {
|
||||
const statusInfo = REFUND_STATUS_MAP[r.status] || {
|
||||
label: '-',
|
||||
color: 'default',
|
||||
tone: 'default' as const,
|
||||
};
|
||||
const realIdx = (refundPage.value - 1) * refundPageSize.value + idx + 1;
|
||||
return (
|
||||
<div key={r.key} class={styles.refundCard}>
|
||||
<div class={styles.refundCardHeader}>
|
||||
<span class={styles.refundIndex}>第{realIdx}次退款</span>
|
||||
<Tag color={statusInfo.color}>{statusInfo.label}</Tag>
|
||||
<StatusTag label={statusInfo.label} tone={statusInfo.tone} />
|
||||
</div>
|
||||
<div class={styles.refundCardBody}>
|
||||
<div class={styles.refundCol}>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
.eventOrdersMain {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.summaryBar {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
color: #1a1a1a;
|
||||
|
||||
.summaryItem {
|
||||
font-weight: 500;
|
||||
|
||||
.summaryLabel {
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tableBody {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
:global {
|
||||
.ant-spin-nested-loading,
|
||||
.ant-spin-container,
|
||||
.ant-table,
|
||||
.ant-table-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
import { useOrderModel, ORDER_STATUS_OPTIONS, REFUND_STATUS_OPTIONS } from './model/useOrderModel';
|
||||
import { useContainerSize, useState } from '@/hooks';
|
||||
import OrderDetailModal from './components/OrderDetailModal';
|
||||
import styles from './index.module.less';
|
||||
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
@@ -176,7 +175,7 @@ export default defineComponent({
|
||||
<Select
|
||||
value={filterForm.orderStatus}
|
||||
options={ORDER_STATUS_OPTIONS as any}
|
||||
style={{ width: '120px' }}
|
||||
style={{ width: '140px' }}
|
||||
allowClear
|
||||
onUpdate:value={(val: any) => (filterForm.orderStatus = val || '')}
|
||||
/>
|
||||
@@ -185,7 +184,7 @@ export default defineComponent({
|
||||
<Select
|
||||
value={filterForm.refundStatus}
|
||||
options={REFUND_STATUS_OPTIONS as any}
|
||||
style={{ width: '120px' }}
|
||||
style={{ width: '140px' }}
|
||||
allowClear
|
||||
onUpdate:value={(val: any) => (filterForm.refundStatus = val || '')}
|
||||
/>
|
||||
@@ -203,24 +202,29 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 表格区 ===== */}
|
||||
<div class={pageStyles.table}>
|
||||
<div class={styles.eventOrdersMain}>
|
||||
{/* 金额汇总 */}
|
||||
<div class={styles.summaryBar}>
|
||||
<span class={styles.summaryItem}>
|
||||
<span class={styles.summaryLabel}>总订单金额(已支付):</span>
|
||||
<div class={pageStyles.summary}>
|
||||
<span class={pageStyles.summaryItem}>
|
||||
<span class={pageStyles.summaryLabel}>总订单金额(已支付):</span>
|
||||
<span class={pageStyles.summaryValue}>
|
||||
{summary.value.totalOrderAmount.toFixed(2)}元
|
||||
</span>
|
||||
<span class={styles.summaryItem}>
|
||||
<span class={styles.summaryLabel}>总实付金额:</span>
|
||||
</span>
|
||||
<span class={pageStyles.summaryItem}>
|
||||
<span class={pageStyles.summaryLabel}>总实付金额:</span>
|
||||
<span class={pageStyles.summaryValue}>
|
||||
{summary.value.totalPaidAmount.toFixed(2)}元
|
||||
</span>
|
||||
<span class={styles.summaryItem}>
|
||||
<span class={styles.summaryLabel}>总退款金额:</span>
|
||||
</span>
|
||||
<span class={pageStyles.summaryItem}>
|
||||
<span class={pageStyles.summaryLabel}>总退款金额:</span>
|
||||
<span class={pageStyles.summaryValue}>
|
||||
{summary.value.totalRefundAmount.toFixed(2)}元
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div ref={containerRef} class={styles.tableBody}>
|
||||
<div ref={containerRef} class={pageStyles.tableBody}>
|
||||
<Table
|
||||
columns={tableColumns}
|
||||
dataSource={dataSource.value}
|
||||
@@ -252,7 +256,6 @@ export default defineComponent({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ===== 详情弹窗 ===== */}
|
||||
{detailVisible.value && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
|
||||
// ============================================================
|
||||
@@ -22,18 +23,18 @@ export const REFUND_STATUS_OPTIONS = [
|
||||
{ value: 'refund_failed', label: '退款失败' },
|
||||
] as const;
|
||||
|
||||
/** 订单状态 Tag 映射 */
|
||||
const ORDER_STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
paid: { label: '已支付', color: 'green' },
|
||||
partial_refund: { label: '部分退款', color: 'orange' },
|
||||
full_refund: { label: '全部退款', color: 'blue' },
|
||||
closed: { label: '已关闭', color: 'default' },
|
||||
/** 订单状态 StatusTag 映射 */
|
||||
const ORDER_STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
paid: { label: '已支付', tone: 'success' },
|
||||
partial_refund: { label: '部分退款', tone: 'orange' },
|
||||
full_refund: { label: '全部退款', tone: 'primary' },
|
||||
closed: { label: '已关闭', tone: 'default' },
|
||||
};
|
||||
|
||||
/** 退款状态 Tag 映射 */
|
||||
const REFUND_STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
refund_success: { label: '退款成功', color: 'green' },
|
||||
refund_failed: { label: '退款失败', color: 'red' },
|
||||
/** 退款状态 StatusTag 映射 */
|
||||
const REFUND_STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
refund_success: { label: '退款成功', tone: 'success' },
|
||||
refund_failed: { label: '退款失败', tone: 'danger' },
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
@@ -254,8 +255,8 @@ export function useOrderModel() {
|
||||
width: 100,
|
||||
align: 'center' as const,
|
||||
customRender: ({ text }: { text: string }) => {
|
||||
const info = ORDER_STATUS_MAP[text] || { label: text || '-', color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = ORDER_STATUS_MAP[text] || { label: text || '-', tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -266,8 +267,8 @@ export function useOrderModel() {
|
||||
align: 'center' as const,
|
||||
customRender: ({ text }: { text: string }) => {
|
||||
if (!text) return h('span', '-');
|
||||
const info = REFUND_STATUS_MAP[text] || { label: text, color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = REFUND_STATUS_MAP[text] || { label: text, tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
.eventUsersMain {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tableBody {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
:global {
|
||||
.ant-spin-nested-loading,
|
||||
.ant-spin-container,
|
||||
.ant-table,
|
||||
.ant-table-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
} from 'ant-design-vue';
|
||||
import { useUserModel, USER_STATUS_OPTIONS } from './model/useUserModel';
|
||||
import { useContainerSize } from '@/hooks';
|
||||
import styles from './index.module.less';
|
||||
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||
|
||||
/**
|
||||
@@ -145,8 +144,7 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 表格区 ===== */}
|
||||
<div class={pageStyles.table}>
|
||||
<div class={styles.eventUsersMain}>
|
||||
<div ref={containerRef} class={styles.tableBody}>
|
||||
<div ref={containerRef} class={pageStyles.tableBody}>
|
||||
<Table
|
||||
columns={tableColumns}
|
||||
dataSource={dataSource.value}
|
||||
@@ -178,7 +176,6 @@ export default defineComponent({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
|
||||
// ============================================================
|
||||
@@ -13,10 +14,10 @@ export const USER_STATUS_OPTIONS = [
|
||||
{ value: 'disabled', label: '禁用' },
|
||||
] as const;
|
||||
|
||||
/** 用户状态 Tag 映射 */
|
||||
const USER_STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
active: { label: '正常', color: 'green' },
|
||||
disabled: { label: '禁用', color: 'red' },
|
||||
/** 用户状态 StatusTag 映射 */
|
||||
const USER_STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
active: { label: '正常', tone: 'success' },
|
||||
disabled: { label: '禁用', tone: 'danger' },
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
@@ -94,8 +95,8 @@ export function useUserModel() {
|
||||
width: 90,
|
||||
align: 'center' as const,
|
||||
customRender: ({ text }: { text: string }) => {
|
||||
const info = USER_STATUS_MAP[text] || { label: text, color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = USER_STATUS_MAP[text] || { label: text, tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
},
|
||||
},
|
||||
{ title: '登录时间', dataIndex: 'loginTime', key: 'loginTime', width: 170 },
|
||||
|
||||
@@ -101,10 +101,10 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
// ===== 柱状图(echarts) =====
|
||||
// ===== 组合图(echarts) =====
|
||||
.chartEcharts {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
// ===== 明细表卡(按内容自适应高度,不被压缩) =====
|
||||
|
||||
@@ -10,7 +10,7 @@ import styles from './index.module.less';
|
||||
* 页面结构:
|
||||
* 1. 顶部标题 + 时间范围选择
|
||||
* 2. 三张统计卡(总收入 / 总提现 / 平台余额)
|
||||
* 3. 月度收支趋势(近 6 个月,echarts 柱状图)
|
||||
* 3. 月度收支趋势(近 6 个月,echarts 柱状 + 折线组合图)
|
||||
* 4. 月度收支明细(表格)
|
||||
*/
|
||||
export default defineComponent({
|
||||
@@ -27,7 +27,7 @@ export default defineComponent({
|
||||
RANGE_OPTIONS,
|
||||
} = useReportsModel();
|
||||
|
||||
// ===== 柱状图(echarts) =====
|
||||
// ===== 组合图(柱状 + 折线,echarts) =====
|
||||
const chartRef = ref<HTMLElement>();
|
||||
let chartInstance: any = null;
|
||||
|
||||
@@ -39,48 +39,90 @@ export default defineComponent({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
type: 'cross',
|
||||
crossStyle: { color: 'rgba(0, 0, 0, 0.15)' },
|
||||
label: { backgroundColor: '#6a7985' },
|
||||
},
|
||||
valueFormatter: (v: number) => `¥${formatMoney(v)}`,
|
||||
},
|
||||
legend: {
|
||||
data: ['订单金额', '净收入'],
|
||||
data: [
|
||||
{ name: '订单金额', icon: 'roundRect' },
|
||||
{ name: '净收入', icon: 'circle' },
|
||||
],
|
||||
bottom: 8,
|
||||
icon: 'roundRect',
|
||||
itemWidth: 14,
|
||||
itemHeight: 10,
|
||||
textStyle: { color: 'rgba(0, 0, 0, 0.65)' },
|
||||
},
|
||||
grid: { left: 8, right: 16, top: 24, bottom: 48, containLabel: true },
|
||||
grid: { left: 8, right: 16, top: 32, bottom: 48, containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: months,
|
||||
axisTick: { show: false },
|
||||
axisLine: { lineStyle: { color: '#e8e8e8' } },
|
||||
axisLabel: { color: 'rgba(0, 0, 0, 0.55)' },
|
||||
axisPointer: { type: 'shadow' },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
formatter: (v: number) => (v >= 1000 ? `${v / 1000}k` : `${v}`),
|
||||
formatter: (v: number) => (v >= 1000 ? `¥${v / 1000}k` : `¥${v}`),
|
||||
},
|
||||
splitLine: { lineStyle: { color: '#f5f5f5' } },
|
||||
splitLine: { lineStyle: { color: '#f5f5f5', type: 'dashed' } },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '订单金额',
|
||||
type: 'bar',
|
||||
data: order,
|
||||
barWidth: 30,
|
||||
itemStyle: { color: '#1677ff', borderRadius: [3, 3, 0, 0] },
|
||||
barMaxWidth: 48,
|
||||
barCategoryGap: '35%',
|
||||
itemStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: '#4096ff' },
|
||||
{ offset: 1, color: '#1677ff' },
|
||||
],
|
||||
},
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
},
|
||||
emphasis: { focus: 'series' },
|
||||
},
|
||||
{
|
||||
name: '净收入',
|
||||
type: 'bar',
|
||||
type: 'line',
|
||||
data: netIncome,
|
||||
barWidth: 30,
|
||||
itemStyle: { color: '#52c41a', borderRadius: [3, 3, 0, 0] },
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
lineStyle: { width: 3, color: '#52c41a' },
|
||||
itemStyle: {
|
||||
color: '#52c41a',
|
||||
borderWidth: 2,
|
||||
borderColor: '#fff',
|
||||
},
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(82, 196, 26, 0.22)' },
|
||||
{ offset: 1, color: 'rgba(82, 196, 26, 0.02)' },
|
||||
],
|
||||
},
|
||||
},
|
||||
emphasis: { focus: 'series' },
|
||||
z: 10,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -170,7 +212,7 @@ export default defineComponent({
|
||||
value={range.value}
|
||||
onUpdate:value={(val: string) => handleRangeChange(val)}
|
||||
options={[...RANGE_OPTIONS]}
|
||||
style={{ width: 120 }}
|
||||
style={{ width: '120px' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -189,7 +231,7 @@ export default defineComponent({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* ===== 柱状图卡(echarts) ===== */}
|
||||
{/* ===== 组合图卡(echarts) ===== */}
|
||||
<div class={styles.chartCard}>
|
||||
<div class={styles.cardTitle}>
|
||||
<span class={styles.cardTitleIcon}>📊</span>
|
||||
|
||||
@@ -17,7 +17,7 @@ export const RANGE_OPTIONS = [
|
||||
// 假数据(数值自洽:总收入 - 总提现 = 平台余额)
|
||||
// ============================================================
|
||||
|
||||
/** 6 个月柱状图数据(月份标签 + 订单金额 + 净收入) */
|
||||
/** 6 个月图表数据(月份标签 + 订单金额 + 净收入) */
|
||||
const CHART_DATA = [
|
||||
{ month: '01月', order: 22500, netIncome: 19000, withdraw: 300 },
|
||||
{ month: '02月', order: 24500, netIncome: 21500, withdraw: 1850 },
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
|
||||
// ============================================================
|
||||
@@ -22,11 +23,11 @@ export const TRANSACTION_STATUS_OPTIONS = [
|
||||
{ value: '已解冻', label: '已解冻' },
|
||||
] as const;
|
||||
|
||||
/** 明细状态映射(用于表格 Tag 渲染) */
|
||||
const TRANSACTION_STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
正常: { label: '正常', color: 'blue' },
|
||||
冻结中: { label: '冻结中', color: 'orange' },
|
||||
已解冻: { label: '已解冻', color: 'green' },
|
||||
/** 明细状态映射(用于表格 StatusTag 渲染) */
|
||||
const TRANSACTION_STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
正常: { label: '正常', tone: 'primary' },
|
||||
冻结中: { label: '冻结中', tone: 'orange' },
|
||||
已解冻: { label: '已解冻', tone: 'success' },
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
@@ -319,10 +320,13 @@ export function useWalletModel() {
|
||||
}
|
||||
};
|
||||
|
||||
/** 渲染交易状态 Tag */
|
||||
/** 渲染交易状态 StatusTag */
|
||||
const renderTransactionStatus = (status: string) => {
|
||||
const info = TRANSACTION_STATUS_MAP[status] || { label: status || '-', color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = TRANSACTION_STATUS_MAP[status] || {
|
||||
label: status || '-',
|
||||
tone: 'default' as const,
|
||||
};
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
.systemRolesMain {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tableBody {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
:global {
|
||||
.ant-spin-nested-loading,
|
||||
.ant-spin-container,
|
||||
.ant-table,
|
||||
.ant-table-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.userCountLink {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.confirmContent {
|
||||
padding: 4px 24px;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
|
||||
strong {
|
||||
margin: 0 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.confirmTip {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.deleteConfirmModal) {
|
||||
.ant-modal-confirm-body {
|
||||
padding: 24px !important;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import { useRoleModel } from './model/useRoleModel';
|
||||
import { useContainerSize } from '@/hooks';
|
||||
import RoleFormModal from './components/RoleFormModal';
|
||||
import UserListModal from './components/UserListModal';
|
||||
import styles from './index.module.less';
|
||||
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||
|
||||
/**
|
||||
@@ -27,7 +26,7 @@ function renderBodyCell({
|
||||
}) {
|
||||
if (column.dataIndex === 'userCount') {
|
||||
return (
|
||||
<a class={styles.userCountLink} onClick={() => onViewUsers(record)}>
|
||||
<a class={pageStyles.tabularLink} onClick={() => onViewUsers(record)}>
|
||||
{record.userCount}人
|
||||
</a>
|
||||
);
|
||||
@@ -55,18 +54,16 @@ function confirmDelete({ record, onOk }: { record: any; onOk: (record: any) => v
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
icon: null,
|
||||
class: styles.deleteConfirmModal,
|
||||
class: 'deleteConfirmModal',
|
||||
content: (
|
||||
<div class={styles.systemRolesMain}>
|
||||
<div class={styles.confirmContent}>
|
||||
<div>
|
||||
<>
|
||||
<div class={pageStyles.confirmContent}>
|
||||
<span>确定要删除角色</span>
|
||||
<strong>{record.roleName}</strong>
|
||||
<span>吗?</span>
|
||||
</div>
|
||||
<div class={styles.confirmTip}>删除后该角色下的用户将失去对应的权限,请谨慎操作。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={pageStyles.confirmTip}>删除后该角色下的用户将失去对应的权限,请谨慎操作。</div>
|
||||
</>
|
||||
),
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@@ -150,8 +147,7 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 表格区 ===== */}
|
||||
<div class={pageStyles.table}>
|
||||
<div class={styles.systemRolesMain}>
|
||||
<div ref={containerRef} class={styles.tableBody}>
|
||||
<div ref={containerRef} class={pageStyles.tableBody}>
|
||||
<Table
|
||||
columns={tableColumns}
|
||||
dataSource={dataSource.value}
|
||||
@@ -184,7 +180,6 @@ export default defineComponent({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ===== 角色表单弹窗 ===== */}
|
||||
{formVisible.value && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
|
||||
// ============================================================
|
||||
@@ -132,9 +133,9 @@ const MOCK_USERS: Record<string, any[]> = {
|
||||
// ============================================================
|
||||
// 状态文本/颜色映射
|
||||
// ============================================================
|
||||
const STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
active: { label: '正常', color: 'green' },
|
||||
disabled: { label: '禁用', color: 'red' },
|
||||
const STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
active: { label: '正常', tone: 'success' },
|
||||
disabled: { label: '禁用', tone: 'danger' },
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
@@ -288,10 +289,10 @@ export function useRoleModel() {
|
||||
/** 根据 roleId 获取该角色下的用户列表 */
|
||||
const getRoleUsers = (roleId: string) => MOCK_USERS[roleId] || [];
|
||||
|
||||
/** 状态 Tag 渲染 */
|
||||
/** 状态 StatusTag 渲染 */
|
||||
const renderStatus = (status: string) => {
|
||||
const info = STATUS_MAP[status] || { label: status || '-', color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = STATUS_MAP[status] || { label: status || '-', tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
.systemUsersMain {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tableBody {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
:global {
|
||||
.ant-spin-nested-loading,
|
||||
.ant-spin-container,
|
||||
.ant-table,
|
||||
.ant-table-container {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirmContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
|
||||
strong {
|
||||
margin: 0 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(.toggleConfirmModal) {
|
||||
.ant-modal-confirm-body {
|
||||
padding: 24px !important;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { Button, Input, Table, Form, Space, Select, Modal, Pagination } from 'an
|
||||
import { useUserModel, USER_ROLE_OPTIONS, USER_STATUS_OPTIONS } from './model/useUserModel';
|
||||
import { useContainerSize } from '@/hooks';
|
||||
import UserFormModal from './components/UserFormModal';
|
||||
import styles from './index.module.less';
|
||||
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||
|
||||
/**
|
||||
@@ -52,15 +51,13 @@ function confirmToggleStatus({ record, onOk }: { record: any; onOk: (record: any
|
||||
const actionText = isActive ? '禁用' : '启用';
|
||||
Modal.confirm({
|
||||
title: `确认${actionText}`,
|
||||
class: styles.toggleConfirmModal,
|
||||
class: 'toggleConfirmModal',
|
||||
content: (
|
||||
<div class={styles.systemUsersMain}>
|
||||
<div class={styles.confirmContent}>
|
||||
<div class={pageStyles.confirmContent}>
|
||||
<span>确定要{actionText}用户</span>
|
||||
<strong>{record.realName}</strong>
|
||||
<span>吗?</span>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
@@ -136,7 +133,7 @@ export default defineComponent({
|
||||
<Select
|
||||
value={filterForm.role}
|
||||
options={USER_ROLE_OPTIONS as any}
|
||||
style={{ width: '120px' }}
|
||||
style={{ width: '140px' }}
|
||||
allowClear
|
||||
onUpdate:value={(val: any) => (filterForm.role = val || '')}
|
||||
/>
|
||||
@@ -145,7 +142,7 @@ export default defineComponent({
|
||||
<Select
|
||||
value={filterForm.status}
|
||||
options={USER_STATUS_OPTIONS as any}
|
||||
style={{ width: '120px' }}
|
||||
style={{ width: '140px' }}
|
||||
allowClear
|
||||
onUpdate:value={(val: any) => (filterForm.status = val || '')}
|
||||
/>
|
||||
@@ -166,8 +163,7 @@ export default defineComponent({
|
||||
|
||||
{/* ===== 表格区 ===== */}
|
||||
<div class={pageStyles.table}>
|
||||
<div class={styles.systemUsersMain}>
|
||||
<div ref={containerRef} class={styles.tableBody}>
|
||||
<div ref={containerRef} class={pageStyles.tableBody}>
|
||||
<Table
|
||||
columns={tableColumns}
|
||||
dataSource={dataSource.value}
|
||||
@@ -199,7 +195,6 @@ export default defineComponent({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ===== 新增/编辑弹窗 ===== */}
|
||||
{modalVisible.value && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { StatusTag, type StatusTagTone } from '@/components';
|
||||
import { useState, useDebounce, useThrottleFn } from '@/hooks';
|
||||
|
||||
// ============================================================
|
||||
@@ -19,17 +20,17 @@ export const USER_STATUS_OPTIONS = [
|
||||
{ value: 'disabled', label: '禁用' },
|
||||
] as const;
|
||||
|
||||
/** 角色文本/颜色映射(表格渲染) */
|
||||
const ROLE_MAP: Record<string, { label: string; color: string }> = {
|
||||
admin: { label: '管理员', color: 'blue' },
|
||||
operator: { label: '运营人员', color: 'cyan' },
|
||||
auditor: { label: '审核人员', color: 'purple' },
|
||||
/** 角色文本/色调映射(表格渲染) */
|
||||
const ROLE_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
admin: { label: '管理员', tone: 'primary' },
|
||||
operator: { label: '运营人员', tone: 'cyan' },
|
||||
auditor: { label: '审核人员', tone: 'purple' },
|
||||
};
|
||||
|
||||
/** 状态文本/颜色映射 */
|
||||
const STATUS_MAP: Record<string, { label: string; color: string }> = {
|
||||
active: { label: '正常', color: 'green' },
|
||||
disabled: { label: '禁用', color: 'red' },
|
||||
/** 状态文本/色调映射 */
|
||||
const STATUS_MAP: Record<string, { label: string; tone: StatusTagTone }> = {
|
||||
active: { label: '正常', tone: 'success' },
|
||||
disabled: { label: '禁用', tone: 'danger' },
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
@@ -103,10 +104,9 @@ export function useUserModel() {
|
||||
dataIndex: 'role',
|
||||
key: 'role',
|
||||
width: 120,
|
||||
align: 'center' as const,
|
||||
customRender: ({ text }: { text: string }) => {
|
||||
const info = ROLE_MAP[text] || { label: text || '-', color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = ROLE_MAP[text] || { label: text || '-', tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -116,8 +116,8 @@ export function useUserModel() {
|
||||
width: 100,
|
||||
align: 'center' as const,
|
||||
customRender: ({ text }: { text: string }) => {
|
||||
const info = STATUS_MAP[text] || { label: text || '-', color: 'default' };
|
||||
return h(Tag, { color: info.color }, () => info.label);
|
||||
const info = STATUS_MAP[text] || { label: text || '-', tone: 'default' as const };
|
||||
return h(StatusTag, { label: info.label, tone: info.tone });
|
||||
},
|
||||
},
|
||||
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 180 },
|
||||
|
||||
Reference in New Issue
Block a user