重新整理仓库

This commit is contained in:
2025-07-25 13:03:01 +08:00
commit 469af7f7f9
979 changed files with 171962 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑班级' : '添加班级'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="选择年级" name="gradeId">
<a-select
v-model:value="form.gradeName"
show-search
placeholder="选择年级"
style="width: 200px"
:options="options"
@change="handleChange"
></a-select>
</a-form-item>
<a-form-item label="班级" name="name">
<a-input
allow-clear
placeholder="请输入班级"
v-model:value="form.name"
@pressEnter="save"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxClass, updateBszxClass } from '@/api/bszx/bszxClass';
import { BszxClass } from '@/api/bszx/bszxClass/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import {BszxGrade} from "@/api/bszx/bszxGrade/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxClass | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
const options = ref<BszxGrade[]>([]);
// 用户信息
const form = reactive<BszxClass>({
id: undefined,
name: undefined,
gradeId: undefined,
gradeName: undefined,
branch: 2,
sortNumber: 100,
comments: undefined,
status: undefined,
tenantId: undefined,
createTime: undefined
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxClassName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-班级名称',
trigger: 'blur'
}
]
});
const handleChange = (value: string, item: any) => {
form.gradeName = value;
form.gradeId = item.id;
};
const handleBranch = () => {
getBszxGradeList();
}
const getBszxGradeList = () => {
listBszxGrade({branch: form.branch}).then((list) => {
options.value = list.map(d => {
d.value = d.name;
d.label = d.name;
return d;
});
})
}
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxClass : addBszxClass;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
getBszxGradeList()
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
// resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,98 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined/>
</template>
<span>添加</span>
</a-button>
<a-select
show-search
v-model:value="where.gradeId"
style="width: 240px"
placeholder="选择年级"
:options="gradeList"
@change="onGrade"
></a-select>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 240px"
v-model:value="where.keywords"
@search="handleSearch"
/>
<a-button @click="reset">重置</a-button>
</a-space>
</template>
<script lang="ts" setup>
import {PlusOutlined} from '@ant-design/icons-vue';
import {watch,ref} from 'vue';
import useSearch from "@/utils/use-search";
import {BszxClassParam} from "@/api/bszx/bszxClass/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const gradeList = ref<BszxClassParam[]>([]);
const emit = defineEmits<{
(e: 'search', where?: BszxClassParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where, resetFields} = useSearch<BszxClassParam>({
gradeId: undefined,
eraId: undefined,
branch: undefined,
keywords: ''
});
// 新增
const add = () => {
emit('add');
};
const handleSearch = () => {
emit('search', where);
}
/* 重置 */
const reset = () => {
resetFields();
handleSearch();
};
const onGrade = (gradeId: number) => {
where.gradeId = gradeId;
handleSearch();
}
const reload = () => {
listBszxGrade({}).then(res => {
gradeList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
}
reload();
watch(
() => props.selection,
() => {
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,237 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<a-button
type="text"
@click="openUrl('/bsyx/grade')"
>年级设置
</a-button
>
<a-button
type="text"
@click="openUrl('/bsyx/class')"
>班级设置
</a-button
>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'era'">
<span>{{ record.era }}</span>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxClassEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxClassEdit from './components/bszxClassEdit.vue';
import {pageBszxClass, removeBszxClass, removeBatchBszxClass} from '@/api/bszx/bszxClass';
import type {BszxClass, BszxClassParam} from '@/api/bszx/bszxClass/model';
import {getPageTitle, openUrl} from "@/utils/common";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxClass[]>([]);
// 当前编辑数据
const current = ref<BszxClass | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxClass({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
// {
// title: 'ID',
// dataIndex: 'id',
// key: 'id',
// width: 120,
// },
{
title: '年级',
dataIndex: 'gradeName',
key: 'gradeName',
width: 120
},
// {
// title: '年级',
// dataIndex: 'gradeId',
// key: 'gradeId',
// width: 120
// },
{
title: '班级',
dataIndex: 'name',
key: 'name'
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: BszxClassParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxClass) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxClass) => {
const hide = message.loading('请求中..', 0);
removeBszxClass(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxClass(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxClass) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxClass'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,164 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑年级' : '添加年级'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="年级" name="name">
<a-input
allow-clear
placeholder="请输入年级"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxGrade, updateBszxGrade } from '@/api/bszx/bszxGrade';
import { BszxGrade } from '@/api/bszx/bszxGrade/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxGrade | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxGrade>({
id: undefined,
branch: 2,
name: undefined,
comments: undefined,
status: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxGradeName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-年级名称',
trigger: 'blur'
}
]
});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxGrade : addBszxGrade;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,52 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import useSearch from "@/utils/use-search";
import { watch } from 'vue';
import {BszxGradeParam} from "@/api/bszx/bszxGrade/model";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: BszxGradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where} = useSearch<BszxGradeParam>({
branch: undefined,
keywords: ''
});
// 新增
const add = () => {
emit('add');
};
const handleSearch = () => {
emit('search', where);
}
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,230 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<a-button
type="text"
@click="openUrl('/bsyx/grade')"
>年级设置
</a-button
>
<a-button
type="text"
@click="openUrl('/bsyx/class')"
>班级设置
</a-button
>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="appBszxGradeId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxGradeEdit v-model:visible="showEdit" :data="current" @done="reload" />
</a-page-header>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxGradeEdit from './components/bszxGradeEdit.vue';
import { pageBszxGrade, removeBszxGrade, removeBatchBszxGrade } from '@/api/bszx/bszxGrade';
import type { BszxGrade, BszxGradeParam } from '@/api/bszx/bszxGrade/model';
import {getPageTitle, openUrl} from "@/utils/common";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxGrade[]>([]);
// 当前编辑数据
const current = ref<BszxGrade | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxGrade({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
// {
// title: 'ID',
// dataIndex: 'id',
// key: 'id',
// align: 'center',
// width: 90,
// },
{
title: '年级',
dataIndex: 'name',
key: 'name'
},
{
title: '排序',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
width: 120,
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: BszxGradeParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxGrade) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxGrade) => {
const hide = message.loading('请求中..', 0);
removeBszxGrade(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxGrade(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxGrade) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxGrade'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,249 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑捐款记录' : '添加捐款记录'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="年龄" name="age">
<a-input
allow-clear
placeholder="请输入年龄"
v-model:value="form.age"
/>
</a-form-item>
<a-form-item label="姓名" name="name">
<a-input
allow-clear
placeholder="请输入姓名"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="性别 1男 2女" name="sex">
<a-input
allow-clear
placeholder="请输入性别 1男 2女"
v-model:value="form.sex"
/>
</a-form-item>
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="班级" name="className">
<a-input
allow-clear
placeholder="请输入班级"
v-model:value="form.className"
/>
</a-form-item>
<a-form-item label="年级" name="gradeName">
<a-input
allow-clear
placeholder="请输入年级"
v-model:value="form.gradeName"
/>
</a-form-item>
<a-form-item label="居住地址" name="address">
<a-input
allow-clear
placeholder="请输入居住地址"
v-model:value="form.address"
/>
</a-form-item>
<a-form-item label="工作单位" name="workUnit">
<a-input
allow-clear
placeholder="请输入工作单位"
v-model:value="form.workUnit"
/>
</a-form-item>
<a-form-item label="职务" name="position">
<a-input
allow-clear
placeholder="请输入职务"
v-model:value="form.position"
/>
</a-form-item>
<a-form-item label="捐赠证书" name="certificate">
<a-input
allow-clear
placeholder="请输入捐赠证书"
v-model:value="form.certificate"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPay, updateBszxPay } from '@/api/bszx/bszxPay';
import { BszxPay } from '@/api/bszx/bszxPay/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPay | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPay>({
id: undefined,
age: undefined,
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
number: undefined,
extra: undefined,
dateTime: undefined,
certificate: undefined,
formData: undefined,
formId: undefined,
userId: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxPayName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-捐款记录名称',
trigger: 'blur'
}
]
});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPay : addBszxPay;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,566 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
:width="`80%`"
:visible="visible"
:confirm-loading="loading"
:maxable="maxAble"
:title="isUpdate ? '编辑订单' : '订单详情'"
:body-style="{ paddingBottom: '8px', background: '#f3f3f3' }"
@update:visible="updateVisible"
:maskClosable="false"
:footer="null"
@ok="save"
>
<a-card style="margin-bottom: 20px" :bordered="false">
<a-descriptions :column="3">
<!-- 第一排-->
<a-descriptions-item
label="订单编号"
span="3"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<span @click="copyText(form.orderNo)">{{ form.orderNo }}</span>
</a-descriptions-item>
<!-- 第二排-->
<a-descriptions-item
label="买家信息"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-space class="flex items-center">
<a-avatar :src="form.avatar" size="small"/>
{{ form.realName }}
</a-space>
</a-descriptions-item>
<a-descriptions-item
label="订单金额"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.totalPrice }}
</a-descriptions-item>
<a-descriptions-item
label="订单状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.orderStatus === 0">未完成</a-tag>
<a-tag v-if="form.orderStatus === 1" color="green">已完成</a-tag>
<a-tag v-if="form.orderStatus === 2" color="red">已取消</a-tag>
<a-tag v-if="form.orderStatus === 3" color="red">取消中</a-tag>
<a-tag v-if="form.orderStatus === 4" color="red">退款申请中</a-tag>
<a-tag v-if="form.orderStatus === 5" color="red">退款被拒绝</a-tag>
<a-tag v-if="form.orderStatus === 6" color="orange">退款成功</a-tag>
<a-tag v-if="form.orderStatus === 7" color="pink">客户端申请退款</a-tag>
</a-descriptions-item>
<!-- 第三排-->
<a-descriptions-item
label="手机号码"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.phone }}
</a-descriptions-item>
<a-descriptions-item
label="实付金额"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.payPrice }}
</a-descriptions-item>
<a-descriptions-item
label="支付状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.payStatus == 1" color="green">已付款</a-tag>
<a-tag v-if="form.payStatus == 0">未付款</a-tag>
<a-tag v-if="form.payStatus == 3">未付款,占场中</a-tag>
</a-descriptions-item>
<!-- 第四排-->
<a-descriptions-item
label="收货地址"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.address }}
</a-descriptions-item>
<a-descriptions-item
label="减少的金额"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.reducePrice }}
</a-descriptions-item>
<a-descriptions-item
label="核销状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.deliveryStatus == 10">未核销</a-tag>
<a-tag v-if="form.deliveryStatus == 20" color="green">已核销</a-tag>
<a-tag v-if="form.deliveryStatus == 30" color="bule">部分核销</a-tag>
</a-descriptions-item>
<!-- 第五排-->
<a-descriptions-item
label="备注信息"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.comments }}
</a-descriptions-item>
<a-descriptions-item
label="支付方式"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tooltip :title="`支付时间:${form.payTime || ''}`">
<template v-if="form.payStatus == 1">
<a-tag v-if="form.payType == 0">余额支付</a-tag>
<a-tag v-if="form.payType == 1">
<WechatOutlined class="tag-icon"/>
微信支付
</a-tag>
<a-tag v-if="form.payType == 2">积分</a-tag>
<a-tag v-if="form.payType == 3">
<AlipayCircleOutlined class="tag-icon"/>
支付宝
</a-tag>
<a-tag v-if="form.payType == 4">
<IdcardOutlined class="tag-icon"/>
现金
</a-tag>
<a-tag v-if="form.payType == 5">
<IdcardOutlined class="tag-icon"/>
POS机
</a-tag>
<a-tag v-if="form.payType == 6">
<IdcardOutlined class="tag-icon"/>
VIP月卡
</a-tag>
<a-tag v-if="form.payType == 7">
<IdcardOutlined class="tag-icon"/>
VIP年卡
</a-tag>
<a-tag v-if="form.payType == 8">
<IdcardOutlined class="tag-icon"/>
VIP次卡
</a-tag>
<a-tag v-if="form.payType == 9">
<IdcardOutlined class="tag-icon"/>
IC月卡
</a-tag>
<a-tag v-if="form.payType == 10">
<IdcardOutlined class="tag-icon"/>
IC年卡
</a-tag>
<a-tag v-if="form.payType == 11">
<IdcardOutlined class="tag-icon"/>
IC次卡
</a-tag>
<a-tag v-if="form.payType == 12">
<IdcardOutlined class="tag-icon"/>
免费
</a-tag>
<a-tag v-if="form.payType == 13">
<IdcardOutlined class="tag-icon"/>
VIP充值卡
</a-tag>
<a-tag v-if="form.payType == 14">
<IdcardOutlined class="tag-icon"/>
IC充值卡
</a-tag>
<a-tag v-if="form.payType == 15">
<IdcardOutlined class="tag-icon"/>
积分支付
</a-tag>
<a-tag v-if="form.payType == 16">
<IdcardOutlined class="tag-icon"/>
VIP季卡
</a-tag>
<a-tag v-if="form.payType == 17">
<IdcardOutlined class="tag-icon"/>
IC季卡
</a-tag>
</template>
</a-tooltip>
</a-descriptions-item>
<a-descriptions-item
label="开票状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.isInvoice == 0">未开具</a-tag>
<a-tag v-if="form.isInvoice == 1" color="green">已开具</a-tag>
<a-tag v-if="form.isInvoice == 2" color="blue">不能开具</a-tag>
</a-descriptions-item>
<!-- 第六排-->
<a-descriptions-item
label="下单时间"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ toDateString(form.createTime, 'yyyy-MM-dd HH:mm') }}
</a-descriptions-item>
<a-descriptions-item
label="交易流水号"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tooltip :title="form.payTime">{{ form.transactionId }}</a-tooltip>
</a-descriptions-item>
<a-descriptions-item
label="结算状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.isSettled == 0">未结算</a-tag>
<a-tag v-if="form.isSettled == 1" color="green">已结算</a-tag>
</a-descriptions-item>
<!-- <a-descriptions-item span="3">-->
<!-- <a-divider/>-->
<!-- </a-descriptions-item>-->
</a-descriptions>
</a-card>
<a-card class="order-card" :bordered="false">
<a-spin :spinning="loading">
<a-table
:data-source="orderInfo"
:columns="columns"
:pagination="false"
/>
</a-spin>
</a-card>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref, reactive, watch} from 'vue';
import {Form} from 'ant-design-vue';
import {assignObject} from 'ele-admin-pro';
import {ColumnItem} from 'ele-admin-pro/es/ele-pro-table/types';
import {
CheckOutlined,
CloseOutlined,
CoffeeOutlined
} from '@ant-design/icons-vue';
import {ShopOrder} from '@/api/shop/shopOrder/model';
import {BszxPay} from '@/api/bszx/bszxPay/model';
import {pageBszxPay} from '@/api/bszx/bszxPay';
import {toDateString} from 'ele-admin-pro';
import {copyText} from "@/utils/common";
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: ShopOrder | null;
}>();
export interface step {
title?: String | undefined;
subTitle?: String | undefined;
description?: String | undefined;
}
// 是否是修改
const isUpdate = ref(false);
// 是否显示最大化切换按钮
const maxAble = ref(true);
// 订单信息
const orderInfo = ref<BszxPay[]>([]);
// 步骤条
const steps = ref<step[]>([
{
title: '报餐',
description: undefined
},
{
title: '付款',
description: undefined
},
{
title: '发餐',
description: undefined
},
{
title: '取餐',
description: undefined
},
{
title: '完成',
description: undefined
}
]);
const active = ref(2);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 订单信息
const form = reactive<ShopOrder>({
// 订单号
orderId: undefined,
// 订单编号
orderNo: undefined,
// 订单类型0商城订单 1预定订单/外卖 2会员卡
type: undefined,
// 快递/自提
deliveryType: undefined,
// 下单渠道0小程序预定 1俱乐部训练场 3活动订场
channel: undefined,
// 微信支付订单号
transactionId: undefined,
// 微信退款订单号
refundOrder: undefined,
// 商户ID
merchantId: undefined,
// 商户名称
merchantName: undefined,
// 商户编号
merchantCode: undefined,
// 使用的优惠券id
couponId: undefined,
// 使用的会员卡id
cardId: undefined,
// 关联管理员id
adminId: undefined,
// 核销管理员id
confirmId: undefined,
// IC卡号
icCard: undefined,
// 头像
avatar: undefined,
// 真实姓名
realName: undefined,
// 手机号码
phone: undefined,
// 收货地址
address: undefined,
//
addressLat: undefined,
//
addressLng: undefined,
// 自提店铺id
selfTakeMerchantId: undefined,
// 自提店铺
selfTakeMerchantName: undefined,
// 配送开始时间
sendStartTime: undefined,
// 配送结束时间
sendEndTime: undefined,
// 发货店铺id
expressMerchantId: undefined,
// 发货店铺
expressMerchantName: undefined,
// 订单总额
totalPrice: undefined,
// 减少的金额使用VIP会员折扣、优惠券抵扣、优惠券折扣后减去的价格
reducePrice: undefined,
// 实际付款
payPrice: undefined,
// 用于统计
price: undefined,
// 价钱,用于积分赠送
money: undefined,
// 退款金额
refundMoney: undefined,
// 教练价格
coachPrice: undefined,
// 购买数量
totalNum: undefined,
// 教练id
coachId: undefined,
// 支付的用户id
payUserId: undefined,
// 0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
payType: undefined,
// 代付支付方式,0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
friendPayType: undefined,
// 0未付款1已付款
payStatus: undefined,
// 0未使用1已完成2已取消3取消中4退款申请中5退款被拒绝6退款成功7客户端申请退款
orderStatus: undefined,
// 发货状态(10未发货 20已发货 30部分发货)
deliveryStatus: undefined,
// 发货时间
deliveryTime: undefined,
// 优惠类型0无、1抵扣优惠券、2折扣优惠券、3、VIP月卡、4VIP年卡5VIP次卡、6VIP会员卡、7IC月卡、8IC年卡、9IC次卡、10IC会员卡、11免费订单、12VIP充值卡、13IC充值卡、14VIP季卡、15IC季卡
couponType: undefined,
// 优惠说明
couponDesc: undefined,
// 二维码地址,保存订单号,支付成功后才生成
qrcode: undefined,
// vip月卡年卡、ic月卡年卡回退次数
returnNum: undefined,
// vip充值回退金额
returnMoney: undefined,
// 预约详情开始时间数组
startTime: undefined,
// 是否已开具发票0未开发票1已开发票2不能开具发票
isInvoice: undefined,
// 发票流水号
invoiceNo: undefined,
// 支付时间
payTime: undefined,
// 退款时间
refundTime: undefined,
// 申请退款时间
refundApplyTime: undefined,
// 过期时间
expirationTime: undefined,
// 对账情况0=未对账1=已对账3=已对账金额对不上4=未查询到该订单
checkBill: undefined,
// 订单是否已结算(0未结算 1已结算)
isSettled: undefined,
// 系统版本号 0当前版本 value=其他版本
version: undefined,
// 用户id
userId: undefined,
// 备注
comments: undefined,
// 排序号
sortNumber: undefined,
// 是否删除, 0否, 1是
deleted: undefined,
// 租户id
tenantId: undefined,
// 修改时间
updateTime: undefined,
// 创建时间
createTime: undefined,
// 自提码
selfTakeCode: undefined,
// 是否已收到赠品
hasTakeGift: undefined,
});
// 请求状态
const loading = ref(true);
const {resetFields} = useForm(form);
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const columns = ref<ColumnItem[]>([
{
title: '商品名称',
dataIndex: 'formName',
key: 'formName',
align: 'center',
width: 280
},
{
title: '金额',
dataIndex: 'price',
align: 'center'
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center'
},
{
title: '操作',
dataIndex: 'isFree',
align: 'center'
}
]);
/* 制作步骤条 */
const loadSteps = (order) => {
steps.value = [];
steps.value.push({
title: '下单'
});
steps.value.push({
title: '付款'
});
steps.value.push({
title: '发货'
});
steps.value.push({
title: '收货'
});
steps.value.push({
title: '完成'
});
// 下单
if (order.payStatus == 10) {
active.value = 0;
steps.value[0].description = order.createTime;
}
// 付款
if (order.payStatus == 20) {
active.value = 1;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
}
// 发货
if (order.payStatus == 20 && order.deliveryStatus == 20) {
active.value = 2;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
steps.value[2].description = order.deliveryTime;
}
// 收货
if (order.payStatus == 20 && order.receiptStatus == 20) {
active.value = 3;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
steps.value[2].description = order.deliveryTime;
steps.value[3].description = order.receiptTime;
}
// 完成
if (order.payStatus == 20 && order.orderStatus == 30) {
active.value = 4;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
steps.value[2].description = order.deliveryTime;
steps.value[3].description = order.receiptTime;
}
// 已取消
if (order.orderStatus == 20) {
active.value = 4;
}
};
// const getOrderInfo = () => {
// const orderId = props.data?.orderId;
// listOrderInfo({ orderId }).then((data) => {
// orderInfo.value = data.filter((d) => d.totalNum > 0);
// });
// };
/* 保存编辑 */
const save = () => {
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = true;
assignObject(form, props.data);
pageBszxPay({orderNo: form.orderNo}).then((res) => {
if (res?.list) {
orderInfo.value = res?.list;
}
loading.value = false;
});
loadSteps(props.data);
}
} else {
resetFields();
}
}
);
</script>
<script lang="ts">
import * as MenuIcons from '@/layout/menu-icons';
export default {
name: 'BszxOrderInfo',
components: MenuIcons
};
</script>

View File

@@ -0,0 +1,204 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-select
v-model:value="where.payStatus"
style="width: 150px"
placeholder="付款状态"
@change="search"
>
<a-select-option value="">全部</a-select-option>
<a-select-option :value="1">已付款</a-select-option>
<a-select-option :value="0">未付款</a-select-option>
</a-select>
<a-select
v-model:value="where.orderStatus"
style="width: 150px"
placeholder="订单状态"
@change="search"
>
<a-select-option value="">全部</a-select-option>
<a-select-option :value="1">已完成</a-select-option>
<a-select-option :value="0">未完成</a-select-option>
<a-select-option :value="2">未使用</a-select-option>
<a-select-option :value="3">已取消</a-select-option>
<a-select-option :value="4">退款中</a-select-option>
<a-select-option :value="5">退款被拒</a-select-option>
<a-select-option :value="6">退款成功</a-select-option>
</a-select>
<a-select
:options="getPayType()"
v-model:value="where.payType"
style="width: 150px"
placeholder="付款方式"
@change="search"
/>
<a-select
v-model:value="where.isInvoice"
style="width: 150px"
placeholder="开票状态"
@change="search"
>
<a-select-option :value="1">已开票</a-select-option>
<a-select-option :value="0">未开票</a-select-option>
<a-select-option :value="2">不能开票</a-select-option>
</a-select>
<a-range-picker
v-model:value="dateRange"
@change="search"
value-format="YYYY-MM-DD"
/>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 280px"
v-model:value="where.keywords"
@search="reload"
/>
<a-button @click="reset">重置</a-button>
<a-button @click="handleExport">导出</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { utils, writeFile } from 'xlsx';
import { message } from 'ant-design-vue';
import useSearch from "@/utils/use-search";
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {listShopOrder} from "@/api/shop/shopOrder";
import {getPayType} from "@/utils/shop";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: ShopOrderParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const { where, resetFields } = useSearch<ShopOrderParam>({
keywords: '',
orderId: undefined,
orderNo: undefined,
createTimeStart: undefined,
createTimeEnd: undefined,
userId: undefined,
phone: undefined,
payStatus: undefined,
orderStatus: undefined,
payType: undefined,
isInvoice: undefined,
});
const reload = () => {
emit('search', where);
};
/* 搜索 */
const search = () => {
const [d1, d2] = dateRange.value ?? [];
xlsFileName.value = `${d1}${d2}`;
where.createTimeStart = d1 ? d1 + ' 00:00:00' : undefined;
where.createTimeEnd = d2 ? d2 + ' 23:59:59' : undefined;
emit('search', {
...where
});
};
/* 重置 */
const reset = () => {
resetFields();
search();
};
const dateRange = ref<[string, string]>(['', '']);
// 变量
const loading = ref(false);
const orders = ref<ShopOrder[]>([])
const xlsFileName = ref<string>();
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'订单编号',
'订单标题',
'买家姓名',
'手机号码',
'实付金额(元)',
'支付方式',
'付款时间',
'下单时间'
]
];
await listShopOrder(where)
.then((list) => {
orders.value = list;
list?.forEach((d: ShopOrder) => {
array.push([
`${d.orderNo}`,
`${d.comments}`,
`${d.realName}`,
`${d.phone}`,
`${d.payPrice}`,
`${getPayType(d.payType)}`,
`${d.payTime || ''}`,
`${d.createTime}`
]);
});
const sheetName = `订单数据`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{ wch: 10 },
{ wch: 40 },
{ wch: 20 },
{ wch: 20 },
{ wch: 60 },
{ wch: 15 },
{ wch: 10 },
{ wch: 10 },
{ wch: 20 },
{ wch: 10 },
{ wch: 20 }
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {});
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,307 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="orderId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div @click="onSearch(record)" class="cursor-pointer">{{ record.name || '匿名' }}</div>
</template>
<template v-if="column.key === 'phone'">
<div v-if="record.mobile" class="text-gray-400">{{ record.mobile }}</div>
<div v-else class="text-gray-600">{{ record.phone }}</div>
</template>
<template v-if="column.key === 'payType'">
<template v-for="item in getPayType()">
<template v-if="record.payStatus == 1">
<span v-if="item.value == record.payType">{{ item.label }}</span>
</template>
<template v-else>
<span></span>
</template>
</template>
</template>
<template v-if="column.key === 'payStatus'">
<a-tag v-if="record.payStatus == 1" color="green" @click="updatePayStatus(record)">已付款</a-tag>
<a-tag v-if="record.payStatus == 0" @click="updatePayStatus(record)">未付款</a-tag>
<a-tag v-if="record.payStatus == 3">未付款,占场中</a-tag>
</template>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'sex'">
<a-tag v-if="record.sex === 1"></a-tag>
<a-tag v-if="record.sex === 2"></a-tag>
</template>
<template v-if="column.key === 'deliveryStatus'">
<a-tag v-if="record.deliveryStatus == 10">未核销</a-tag>
<a-tag v-if="record.deliveryStatus == 20" color="green">已核销</a-tag>
<a-tag v-if="record.deliveryStatus == 30" color="bule">部分核销</a-tag>
</template>
<template v-if="column.key === 'orderStatus'">
<a-tag v-if="record.orderStatus === 0">未完成</a-tag>
<a-tag v-if="record.orderStatus === 1" color="green">已完成</a-tag>
<a-tag v-if="record.orderStatus === 2" color="red">已取消</a-tag>
<a-tag v-if="record.orderStatus === 3" color="red">取消中</a-tag>
<a-tag v-if="record.orderStatus === 4" color="red">退款申请中</a-tag>
<a-tag v-if="record.orderStatus === 5" color="red">退款被拒绝</a-tag>
<a-tag v-if="record.orderStatus === 6" color="orange">退款成功</a-tag>
<a-tag v-if="record.orderStatus === 7" color="pink">客户端申请退款</a-tag>
</template>
<template v-if="column.key === 'isInvoice'">
<a-tag v-if="record.isInvoice == 0">未开具</a-tag>
<a-tag v-if="record.isInvoice == 1" color="green">已开具</a-tag>
<a-tag v-if="record.isInvoice == 2" color="blue">不能开具</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<OrderInfo v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bszx/extra.vue";
import {pageBszxOrder} from "@/api/bszx/bszxOrder";
import OrderInfo from './components/orderInfo.vue';
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {updateShopOrder} from "@/api/shop/shopOrder";
import {message} from "ant-design-vue";
import {updateUser} from "@/api/system/user";
import {getPayType} from '@/utils/shop';
import {repairOrder} from "@/api/bszx/bszxPay";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<ShopOrder[]>([]);
// 当前编辑数据
const current = ref<ShopOrder | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxOrder({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '订单号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center',
width: 200,
},
{
title: '姓名',
dataIndex: 'realName',
key: 'realName',
align: 'center'
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center'
},
{
title: '实付金额',
dataIndex: 'payPrice',
key: 'payPrice',
align: 'center',
customRender: ({text}) => '¥' + text
},
{
title: '支付方式',
dataIndex: 'payType',
key: 'payType',
align: 'center'
},
{
title: '支付状态',
dataIndex: 'payStatus',
key: 'payStatus',
align: 'center'
},
{
title: '核销状态',
dataIndex: 'deliveryStatus',
key: 'deliveryStatus',
align: 'center',
},
{
title: '开票状态',
dataIndex: 'isInvoice',
key: 'isInvoice',
align: 'center',
},
{
title: '订单状态',
dataIndex: 'orderStatus',
key: 'orderStatus',
align: 'center',
},
// {
// title: '支付时间',
// dataIndex: 'payTime',
// key: 'payTime',
// align: 'center',
// width: 180,
// sorter: true,
// ellipsis: true
// },
{
title: '下单时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true,
ellipsis: true
},
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: ShopOrderParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
const onSearch = (item: ShopOrder) => {
reload({userId: item.userId})
}
/* 打开编辑弹窗 */
const openEdit = (row?: ShopOrder) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/**
* 修复订单支付状态
*/
const updatePayStatus = (record: ShopOrder) => {
// 修复订单
repairOrder(record).then(() => {
message.success('修复成功');
}).then(() => {
if(record.realName == '' || record.realName == undefined){
// 更新用户真实姓名
updateUser({
userId: record.userId,
realName: record.realName
})
}
reload();
})
}
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: ShopOrder) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
import * as MenuIcons from '@/layout/menu-icons';
export default {
name: 'BszxOrder',
components: MenuIcons
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,249 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑捐款记录' : '添加捐款记录'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="年龄" name="age">
<a-input
allow-clear
placeholder="请输入年龄"
v-model:value="form.age"
/>
</a-form-item>
<a-form-item label="姓名" name="name">
<a-input
allow-clear
placeholder="请输入姓名"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="性别 1男 2女" name="sex">
<a-input
allow-clear
placeholder="请输入性别 1男 2女"
v-model:value="form.sex"
/>
</a-form-item>
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="班级" name="className">
<a-input
allow-clear
placeholder="请输入班级"
v-model:value="form.className"
/>
</a-form-item>
<a-form-item label="年级" name="gradeName">
<a-input
allow-clear
placeholder="请输入年级"
v-model:value="form.gradeName"
/>
</a-form-item>
<a-form-item label="居住地址" name="address">
<a-input
allow-clear
placeholder="请输入居住地址"
v-model:value="form.address"
/>
</a-form-item>
<a-form-item label="工作单位" name="workUnit">
<a-input
allow-clear
placeholder="请输入工作单位"
v-model:value="form.workUnit"
/>
</a-form-item>
<a-form-item label="职务" name="position">
<a-input
allow-clear
placeholder="请输入职务"
v-model:value="form.position"
/>
</a-form-item>
<a-form-item label="捐赠证书" name="certificate">
<a-input
allow-clear
placeholder="请输入捐赠证书"
v-model:value="form.certificate"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPay, updateBszxPay } from '@/api/bszx/bszxPay';
import { BszxPay } from '@/api/bszx/bszxPay/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPay | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPay>({
id: undefined,
age: undefined,
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
number: undefined,
extra: undefined,
dateTime: undefined,
certificate: undefined,
formData: undefined,
formId: undefined,
userId: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxPayName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-捐款记录名称',
trigger: 'blur'
}
]
});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPay : addBszxPay;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,230 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-select
show-search
v-model:value="where.gradeName"
style="width: 240px"
placeholder="选择年级"
:options="gradeList"
@change="onGrade"
></a-select>
<a-select
v-if="where.gradeName"
show-search
v-model:value="where.className"
style="width: 240px"
placeholder="选择年级"
:options="classList"
@change="onClass"
></a-select>
<a-range-picker
v-model:value="dateRange"
@change="search"
value-format="YYYY-MM-DD"
/>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 220px"
v-model:value="where.keywords"
@search="reload"
/>
<a-button @click="handleExport">导出</a-button>
</a-space>
</template>
<script lang="ts" setup>
import {ref, watch} from 'vue';
import {utils, writeFile} from 'xlsx';
import dayjs from 'dayjs';
import {message} from 'ant-design-vue';
import useSearch from "@/utils/use-search";
import {BszxPayParam} from "@/api/bszx/bszxPay/model";
import {BszxBm} from "@/api/bszx/bszxBm/model";
import {listBszxPay} from "@/api/bszx/bszxPay";
import {BszxClassParam} from "@/api/bszx/bszxClass/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
import {listBszxClass} from "@/api/bszx/bszxClass";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: BszxPayParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where} = useSearch<BszxPayParam>({
id: undefined,
keywords: '',
gradeName: undefined,
className: undefined,
createTimeStart: undefined,
createTimeEnd: undefined,
userId: undefined
});
const reload = () => {
emit('search', where);
};
/* 搜索 */
const search = () => {
const [d1, d2] = dateRange.value ?? [];
where.createTimeStart = d1 ? d1 + ' 00:00:00' : undefined;
where.createTimeEnd = d2 ? d2 + ' 23:59:59' : undefined;
emit('search', {
...where
});
};
const dateRange = ref<[string, string]>(['', '']);
// 变量
const loading = ref(false);
const bmList = ref<BszxBm[]>([])
const xlsFileName = ref<string>();
const branchId = ref<number>();
const gradeId = ref<number>();
const gradeList = ref<BszxClassParam[]>([]);
const classList = ref<BszxClassParam[]>([]);
const getGradeList = () => {
listBszxGrade({branch: branchId.value}).then(res => {
gradeList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
};
const getClassList = () => {
listBszxClass({gradeId: gradeId.value}).then(res => {
classList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
}
const onGrade = (gradeName: number, item: any) => {
where.gradeName = item.name;
if (gradeName) {
console.log(item)
gradeId.value = item.id;
getClassList();
}
emit('search', where);
}
const onBranchId = () => {
getGradeList();
}
const onClass = (classId, item) => {
console.log(classId);
where.className = item.name;
reload();
}
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'订单编号',
'姓名',
'手机号码',
'捐款金额',
'性别',
'年级',
'班级',
'居住地址',
'工作单位',
'职务',
'捐款时间'
]
];
// 按搜索结果导出
where.sceneType = 'Content';
await listBszxPay(where)
.then((list) => {
bmList.value = list;
list?.forEach((d: BszxBm) => {
array.push([
`${d.orderNo}`,
`${d.name}`,
`${d.mobile}`,
`${d.price}`,
`${d.sex == 1 ? '男' : ''}${d.sex == 2 ? '女' : '-'}`,
`${d.gradeName ? d.gradeName : '-'}`,
`${d.className ? d.className : '-'}`,
`${d.address ? d.address : '-'}`,
`${d.workUnit ? d.workUnit : '-'}`,
`${d.position ? d.position : '-'}`,
`${d.createTime}`
]);
});
const sheetName = `导出捐款记录${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10},
{wch: 40},
{wch: 20},
{wch: 20},
{wch: 60},
{wch: 15},
{wch: 10},
{wch: 10},
{wch: 20},
{wch: 10},
{wch: 20}
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {
});
};
getGradeList();
watch(
() => props.totalPriceAmount,
(totalPriceAmount) => {
console.log(totalPriceAmount, 'totalPriceAmount')
}
);
</script>

View File

@@ -0,0 +1,336 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:parse-data="parseData"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div @click="onSearch(record)" class="cursor-pointer">{{ record.name || '匿名' }}</div>
</template>
<template v-if="column.key === 'phone'">
<div v-if="record.mobile" class="text-gray-400">{{ record.mobile }}</div>
<div v-else class="text-gray-600">{{ record.phone }}</div>
</template>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'sex'">
<a-tag v-if="record.sex === 1"></a-tag>
<a-tag v-if="record.sex === 2"></a-tag>
</template>
<template v-if="column.key === 'present'">
<a-tag v-if="record.present"></a-tag>
<a-tag v-else>不能</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
<template #footer>
<span v-if="totalPriceAmount" class="text-red-500 font-bold">小计{{ totalPriceAmount.toFixed(2) }}</span>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxPayEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxPayEdit from './components/bszxPayEdit.vue';
import {pageBszxPay, removeBszxPay, removeBatchBszxPay} from '@/api/bszx/bszxPay';
import type {BszxPay, BszxPayParam} from '@/api/bszx/bszxPay/model';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bsyx/extra.vue";
import {PageResult} from "@/api";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxPay[]>([]);
// 当前编辑数据
const current = ref<BszxPay | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
const totalPriceAmount = ref<number>(0);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxPay({
...where,
...orders,
page,
limit
});
};
// 整理数据
const parseData = (data: PageResult<BszxPay>) => {
totalPriceAmount.value = 0;
data.list?.map((item) => {
if(item.price){
totalPriceAmount.value += Number(item.price)
}
})
return data;
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '订单编号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center',
width: 200,
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center',
width: 120
},
{
title: '捐款金额',
dataIndex: 'price',
key: 'price',
width: 120,
align: 'center',
sorter: true,
customRender: ({text}) => '¥' + text
},
{
title: '性别',
dataIndex: 'sex',
key: 'sex',
align: 'center',
customRender: ({text}) => ['', '男', '女'][text]
},
{
title: '分部',
dataIndex: 'branchName',
key: 'branchName',
align: 'center',
},
{
title: '年级',
dataIndex: 'gradeName',
key: 'gradeName',
align: 'center',
},
{
title: '班级',
dataIndex: 'className',
key: 'className',
align: 'center',
},
{
title: '居住地址',
dataIndex: 'address',
key: 'address',
align: 'center',
},
{
title: '工作单位',
dataIndex: 'workUnit',
key: 'workUnit',
align: 'center',
},
{
title: '职务',
dataIndex: 'position',
key: 'position',
align: 'center',
},
// {
// title: '捐赠证书',
// dataIndex: 'certificate',
// key: 'certificate',
// align: 'center',
// },
{
title: '心愿',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
// {
// title: '状态',
// dataIndex: 'status',
// key: 'status',
// align: 'center',
// },
{
title: '捐款时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true,
ellipsis: true
},
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: BszxPayParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
const onSearch = (item: BszxPay) => {
reload({userId: item.userId})
}
/* 打开编辑弹窗 */
const openEdit = (row?: BszxPay) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxPay) => {
const hide = message.loading('请求中..', 0);
removeBszxPay(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxPay(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxPay) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
// openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxPay'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,220 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑百色中学-捐款排行' : '添加百色中学-捐款排行'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="来源表ID项目名称" name="formId">
<a-input
allow-clear
placeholder="请输入来源表ID项目名称"
v-model:value="form.formId"
/>
</a-form-item>
<a-form-item label="数量" name="number">
<a-input
allow-clear
placeholder="请输入数量"
v-model:value="form.number"
/>
</a-form-item>
<a-form-item label="获得捐款总金额" name="totalPrice">
<a-input
allow-clear
placeholder="请输入获得捐款总金额"
v-model:value="form.totalPrice"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPayRanking, updateBszxPayRanking } from '@/api/bszx/bszxPayRanking';
import { BszxPayRanking } from '@/api/bszx/bszxPayRanking/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPayRanking | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPayRanking>({
id: undefined,
formId: undefined,
number: undefined,
totalPrice: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
bszxPayRankingId: undefined,
bszxPayRankingName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
bszxPayRankingName: [
{
required: true,
type: 'string',
message: '请填写百色中学-捐款排行名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPayRanking : addBszxPayRanking;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,69 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-range-picker
v-model:value="dateRange"
@change="search"
value-format="YYYY-MM-DD"
/>
<a-tooltip title="获得捐款总金额" class="flex px-4">
<span class="text-gray-400">捐款总金额</span>
<span class="text-gray-700 font-bold">{{ formatNumber(totalPriceAmount) }}</span>
</a-tooltip>
</a-space>
</template>
<script lang="ts" setup>
import useSearch from "@/utils/use-search";
import { watch,ref } from 'vue';
import { formatNumber } from 'ele-admin-pro/es';
import {BszxPayRankingParam} from "@/api/bszx/bszxPayRanking/model";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
totalPriceAmount?: number;
}>(),
{}
);
// 日期范围选择
const dateRange = ref<[string, string]>(['', '']);
const emit = defineEmits<{
(e: 'search', where?: BszxPayRankingParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where,resetFields} = useSearch<BszxPayRankingParam>({
id: undefined,
userId: undefined,
createTimeStart: undefined,
createTimeEnd: undefined,
keywords: ''
});
/* 搜索 */
const search = () => {
const [d1, d2] = dateRange.value ?? [];
emit('search', {
...where,
createTimeStart: d1 ? d1 + ' 00:00:00' : '',
createTimeEnd: d2 ? d2 + ' 23:59:59' : ''
});
};
const onSearch = (text: string) => {
where.sceneType = text
search();
}
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,223 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
:totalPriceAmount="totalPriceAmount.toFixed(2)"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxPayRankingEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxPayRankingEdit from './components/bszxPayRankingEdit.vue';
import {removeBszxPayRanking, removeBatchBszxPayRanking, ranking} from '@/api/bszx/bszxPayRanking';
import type {BszxPayRanking, BszxPayRankingParam} from '@/api/bszx/bszxPayRanking/model';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bsyx/extra.vue";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxPayRanking[]>([]);
// 当前编辑数据
const current = ref<BszxPayRanking | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 合计总金额
const totalPriceAmount = ref<number>(0);
// 表格数据源
const datasource: DatasourceFunction = ({where}) => {
return ranking({...where}).then(data => {
totalPriceAmount.value = 0;
data.map((item) => {
if (item.totalPrice) {
totalPriceAmount.value += item.totalPrice
}
})
return data;
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({index}) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '项目名称',
dataIndex: 'formName',
key: 'formName'
},
{
title: '捐款人数',
dataIndex: 'number',
key: 'number',
align: 'center',
},
{
title: '获得捐款总金额',
dataIndex: 'totalPrice',
key: 'totalPrice',
align: 'center',
}
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: BszxPayRankingParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxPayRanking) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxPayRanking) => {
const hide = message.loading('请求中..', 0);
removeBszxPayRanking(row.bszxPayRankingId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxPayRanking(selection.value.map((d) => d.bszxPayRankingId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxPayRanking) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxPayRanking'
};
</script>
<style lang="less" scoped></style>

60
src/views/bsyx/extra.vue Normal file
View File

@@ -0,0 +1,60 @@
<!-- 搜索表单 -->
<template>
<a-space style="flex-wrap: wrap" v-if="hasRole('superAdmin') || hasRole('admin') || hasRole('foundation')">
<a-button
type="text"
@click="openUrl('/bsyx/ranking')"
>捐款排行榜
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import {watch,nextTick} from 'vue';
import {CmsWebsite} from '@/api/cms/cmsWebsite/model';
import {openUrl} from "@/utils/common";
import {message} from 'ant-design-vue';
import {removeSiteInfoCache} from "@/api/cms/cmsWebsite";
import {hasRole} from "@/utils/permission";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
website?: CmsWebsite;
count?: 0;
}>(),
{}
);
const emit = defineEmits<{
(e: 'add'): void;
}>();
const add = () => {
emit('add');
};
// 清除缓存
const clearSiteInfoCache = () => {
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId') + "*").then(
(msg) => {
if (msg) {
message.success(msg);
}
}
);
};
nextTick(() => {
if(localStorage.getItem('NotActive')){
// IsActive.value = false
}
})
watch(
() => props.selection,
() => {
}
);
</script>

View File

@@ -0,0 +1,283 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑报名记录' : '添加报名'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="姓名" name="name">
<a-input
allow-clear
placeholder="请输入姓名"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="类型" name="type">
<a-radio-group v-model:value="form.type">
<a-radio :value="0">校友</a-radio>
<a-radio :value="1">单位</a-radio>
<a-radio :value="2">爱心人士</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="性别" name="sex">
<DictSelect
dict-code="sex"
:placeholder="`请选择性别`"
v-model:value="form.sexName"
@done="chooseSex"
/>
</a-form-item>
<a-form-item label="性别" name="sex">
{{ form }}
{{ form.sex == 1 ? '男' : '' }}
{{ form.sex == 2 ? '女' : '' }}
</a-form-item>
<a-form-item label="手机号码" name="mobile">
<a-input
allow-clear
:disabled="form.mobile"
placeholder="请输入手机号码"
v-model:value="form.mobile"
/>
</a-form-item>
<a-form-item label="班级" name="className">
<a-input
allow-clear
disabled
placeholder="请输入班级"
v-model:value="form.className"
/>
</a-form-item>
<a-form-item label="年级" name="gradeName">
<a-input
allow-clear
disabled
placeholder="请输入年级"
v-model:value="form.gradeName"
/>
</a-form-item>
<a-form-item label="居住地址" name="address">
<a-input
allow-clear
placeholder="请输入居住地址"
v-model:value="form.address"
/>
</a-form-item>
<a-form-item label="工作单位" name="workUnit">
<a-input
allow-clear
placeholder="请输入工作单位"
v-model:value="form.workUnit"
/>
</a-form-item>
<a-form-item label="职务" name="position">
<a-input
allow-clear
placeholder="请输入职务"
v-model:value="form.position"
/>
</a-form-item>
<a-form-item label="是否能到场" name="present">
<a-input
allow-clear
placeholder="请输入是否能到场"
v-model:value="form.present"
/>
</a-form-item>
<!-- <a-form-item label="邀请函" name="certificate">-->
<!-- <a-input-->
<!-- allow-clear-->
<!-- placeholder="请输入邀请函"-->
<!-- v-model:value="form.certificate"-->
<!-- />-->
<!-- </a-form-item>-->
<!-- <a-form-item label="排序" name="sortNumber">-->
<!-- <a-input-number-->
<!-- :min="0"-->
<!-- :max="9999"-->
<!-- class="ele-fluid"-->
<!-- placeholder="请输入排序号"-->
<!-- v-model:value="form.sortNumber"-->
<!-- />-->
<!-- </a-form-item>-->
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<!-- <a-form-item label="状态" name="status">-->
<!-- <a-radio-group v-model:value="form.status">-->
<!-- <a-radio :value="0">显示</a-radio>-->
<!-- <a-radio :value="1">隐藏</a-radio>-->
<!-- </a-radio-group>-->
<!-- </a-form-item>-->
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref, reactive, watch} from 'vue';
import {Form, message} from 'ant-design-vue';
import {assignObject, uuid} from 'ele-admin-pro';
import {addBszxBm, updateBszxBm} from '@/api/bszx/bszxBm';
import {BszxBm} from '@/api/bszx/bszxBm/model';
import {useThemeStore} from '@/store/modules/theme';
import {storeToRefs} from 'pinia';
import {ItemType} from 'ele-admin-pro/es/ele-image-upload/types';
import {FormInstance} from 'ant-design-vue/es/form';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const {styleResponsive} = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxBm | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxBm>({
id: undefined,
name: undefined,
sex: undefined,
sexName: undefined,
phone: undefined,
mobile: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
present: undefined,
age: undefined,
number: undefined,
extra: undefined,
certificate: undefined,
dateTime: undefined,
formData: undefined,
formId: undefined,
userId: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxBmName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-报名记录名称',
trigger: 'blur'
}
]
});
const chooseSex = (data: any) => {
form.sex = data.key;
form.sexName = data.label;
};
const {resetFields} = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxBm : addBszxBm;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.sex){
form.sexName = props.data.sexName;
}
if (props.data.image) {
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{immediate: true}
);
</script>

View File

@@ -0,0 +1,224 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<!-- <a-button type="primary" class="ele-btn-icon" @click="add">-->
<!-- <template #icon>-->
<!-- <PlusOutlined />-->
<!-- </template>-->
<!-- <span>添加</span>-->
<!-- </a-button>-->
<a-radio-group v-model:value="where.branchId" @change="reload">
<a-radio-button :value="1">初中部</a-radio-button>
<a-radio-button :value="2">高中部</a-radio-button>
<a-radio-button :value="8">教职员工</a-radio-button>
<a-radio-button :value="9">其他</a-radio-button>
</a-radio-group>
<a-select
show-search
v-model:value="where.gradeName"
style="width: 240px"
placeholder="选择年级"
:options="gradeList"
@change="onGrade"
></a-select>
<a-select
v-if="where.gradeName"
show-search
v-model:value="where.classId"
style="width: 240px"
placeholder="选择年级"
:options="classList"
@change="onClass"
></a-select>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 280px"
v-model:value="where.keywords"
@search="reload"
/>
<a-button type="text" @click="handleExport">导出</a-button>
</a-space>
</template>
<script lang="ts" setup>
import {ref, watch} from 'vue';
import {utils, writeFile} from 'xlsx';
import dayjs from 'dayjs';
import {message} from 'ant-design-vue';
import {BszxBm, BszxBmParam} from "@/api/bszx/bszxBm/model";
import useSearch from "@/utils/use-search";
import {listBszxBm} from "@/api/bszx/bszxBm";
import {BszxClassParam} from "@/api/bszx/bszxClass/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
import {listBszxClass} from "@/api/bszx/bszxClass";
import { PlusOutlined } from '@ant-design/icons-vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: BszxBmParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where} = useSearch<BszxBmParam>({
id: undefined,
keywords: '',
className: undefined,
classId: undefined,
userId: undefined
});
// 变量
const loading = ref(false);
const bmList = ref<BszxBm[]>([])
const xlsFileName = ref<string>();
const gradeId = ref<number>();
const gradeList = ref<BszxClassParam[]>([]);
const classList = ref<BszxClassParam[]>([]);
const reload = () => {
emit('search', where);
};
// 新增
const add = () => {
emit('add');
};
const getGradeList = () => {
listBszxGrade({branch: where.branchId}).then(res => {
gradeList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
};
const getClassList = () => {
listBszxClass({gradeId: gradeId.value}).then(res => {
classList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
}
const onGrade = (gradeName: number, item: any) => {
where.gradeName = item.name;
if(gradeName){
console.log(item)
gradeId.value = item.id;
getClassList();
}
emit('search', where);
}
const onClass = (classId,item) => {
console.log(classId);
where.className = item.name;
reload();
}
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'用户ID',
'姓名',
'性别',
'手机号码',
'班级',
'年级',
'居住地址',
'工作单位',
'职务',
'是否能到场',
'邀请函',
'报名时间'
]
];
// 按搜索结果导出
where.sceneType = 'Content';
await listBszxBm(where)
.then((list) => {
bmList.value = list;
list?.forEach((d: BszxBm) => {
array.push([
`${d.userId}`,
`${d.name}`,
`${d.sex}`,
`${d.phone}`,
`${d.className}`,
`${d.gradeName}`,
`${d.address}`,
`${d.workUnit}`,
`${d.position}`,
`${d.present}`,
`${d.certificate}`,
`${d.createTime}`
]);
});
const sheetName = `导出报名列表${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10},
{wch: 40},
{wch: 20},
{wch: 20},
{wch: 60},
{wch: 15},
{wch: 10},
{wch: 10},
{wch: 20},
{wch: 10},
{wch: 20}
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {
});
};
getGradeList();
watch(
() => props.selection,
() => {
}
);
</script>

View File

@@ -0,0 +1,306 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<!-- <Extra/>-->
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<a-space>
<a-avatar :src="record.avatar"/>
<span>{{ record.name }}</span>
</a-space>
</template>
<template v-if="column.key === 'sex'">
<a-tag v-if="record.sex === 1"></a-tag>
<a-tag v-if="record.sex === 2"></a-tag>
</template>
<template v-if="column.key === 'present'">
<a-tag v-if="record.present"></a-tag>
<a-tag v-else>不能</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<!-- <a @click="openEdit(record)">修改</a>-->
<!-- <a-divider type="vertical" />-->
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<bszxBmEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxBmEdit from './components/bszxBmEdit.vue';
import {pageBszxBm, removeBszxBm, removeBatchBszxBm} from '@/api/bszx/bszxBm';
import type {BszxBm, BszxBmParam} from '@/api/bszx/bszxBm/model';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bszx/extra.vue";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxBm[]>([]);
// 当前编辑数据
const current = ref<BszxBm | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
// if (filters) {
// where.status = filters.status;
// }
return pageBszxBm({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 90,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({index}) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
width: 120,
align: 'center',
customRender: ({text}) => ['校友', '单位', '爱心人士'][text]
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
width: 120
},
{
title: '手机号码',
dataIndex: 'mobile',
key: 'mobile',
width: 130,
align: 'center',
},
{
title: '性别',
dataIndex: 'sex',
key: 'sex',
width: 90,
align: 'center',
customRender: ({text}) => ['', '男', '女'][text]
},
{
title: '分部',
dataIndex: 'branchName',
key: 'branchName',
align: 'center',
},
{
title: '年级',
dataIndex: 'gradeName',
key: 'gradeName',
align: 'center',
},
{
title: '班级',
dataIndex: 'className',
key: 'className',
align: 'center',
},
{
title: '居住地址',
dataIndex: 'address',
key: 'address',
align: 'center',
},
{
title: '工作单位',
dataIndex: 'workUnit',
key: 'workUnit',
align: 'center',
},
{
title: '职务',
dataIndex: 'position',
key: 'position',
align: 'center',
},
{
title: '是否能到场',
dataIndex: 'present',
key: 'present',
align: 'center',
},
// {
// title: '状态',
// dataIndex: 'status',
// key: 'status',
// align: 'center',
// },
{
title: '报名时间',
dataIndex: 'createTime',
key: 'createTime',
width: 180,
align: 'center',
sorter: true,
ellipsis: true
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: BszxBmParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxBm) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxBm) => {
const hide = message.loading('请求中..', 0);
removeBszxBm(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxBm(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxBm) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxBm'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,179 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑百色中学-分部' : '添加百色中学-分部'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="分部名称 " name="name">
<a-input
allow-clear
placeholder="请输入分部名称 "
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxBranch, updateBszxBranch } from '@/api/bszx/bszxBranch';
import { BszxBranch } from '@/api/bszx/bszxBranch/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxBranch | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxBranch>({
id: undefined,
name: undefined,
sortNumber: undefined,
tenantId: undefined,
bszxBranchId: undefined,
bszxBranchName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
bszxBranchName: [
{
required: true,
type: 'string',
message: '请填写百色中学-分部名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxBranch : addBszxBranch;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,218 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="bszxBranchId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxBranchEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxBranchEdit from './components/bszxBranchEdit.vue';
import { pageBszxBranch, removeBszxBranch, removeBatchBszxBranch } from '@/api/bszx/bszxBranch';
import type { BszxBranch, BszxBranchParam } from '@/api/bszx/bszxBranch/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxBranch[]>([]);
// 当前编辑数据
const current = ref<BszxBranch | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxBranch({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '分部名称 ',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: BszxBranchParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxBranch) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxBranch) => {
const hide = message.loading('请求中..', 0);
removeBszxBranch(row.bszxBranchId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxBranch(selection.value.map((d) => d.bszxBranchId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxBranch) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxBranch'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,205 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑班级' : '添加班级'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="初高中部" name="branch">
<a-radio-group v-model:value="form.branch" @change="handleBranch">
<a-radio-button :value="1">初中部</a-radio-button>
<a-radio-button :value="2">高中部</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="选择年级" name="gradeId">
<a-select
v-model:value="form.gradeName"
show-search
placeholder="选择年级"
style="width: 200px"
:options="options"
@change="handleChange"
></a-select>
</a-form-item>
<a-form-item label="班级" name="name">
<a-input
allow-clear
placeholder="请输入班级"
v-model:value="form.name"
@pressEnter="save"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxClass, updateBszxClass } from '@/api/bszx/bszxClass';
import { BszxClass } from '@/api/bszx/bszxClass/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import {BszxGrade} from "@/api/bszx/bszxGrade/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxClass | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
const options = ref<BszxGrade[]>([]);
// 用户信息
const form = reactive<BszxClass>({
id: undefined,
name: undefined,
gradeId: undefined,
gradeName: undefined,
branch: 2,
sortNumber: 100,
comments: undefined,
status: undefined,
tenantId: undefined,
createTime: undefined
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxClassName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-班级名称',
trigger: 'blur'
}
]
});
const handleChange = (value: string, item: any) => {
form.gradeName = value;
form.gradeId = item.id;
};
const handleBranch = () => {
getBszxGradeList();
}
const getBszxGradeList = () => {
listBszxGrade({branch: form.branch}).then((list) => {
options.value = list.map(d => {
d.value = d.name;
d.label = d.name;
return d;
});
})
}
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxClass : addBszxClass;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
getBszxGradeList()
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
// resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,102 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined/>
</template>
<span>添加</span>
</a-button>
<a-radio-group v-model:value="where.branch" @change="handleSearch">
<a-radio-button :value="1">初中部</a-radio-button>
<a-radio-button :value="2">高中部</a-radio-button>
</a-radio-group>
<a-select
show-search
v-model:value="where.gradeId"
style="width: 240px"
placeholder="选择年级"
:options="gradeList"
@change="onGrade"
></a-select>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 240px"
v-model:value="where.keywords"
@search="handleSearch"
/>
<a-button @click="reset">重置</a-button>
</a-space>
</template>
<script lang="ts" setup>
import {PlusOutlined} from '@ant-design/icons-vue';
import {watch,ref} from 'vue';
import useSearch from "@/utils/use-search";
import {BszxClassParam} from "@/api/bszx/bszxClass/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const gradeList = ref<BszxClassParam[]>([]);
const emit = defineEmits<{
(e: 'search', where?: BszxClassParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where, resetFields} = useSearch<BszxClassParam>({
gradeId: undefined,
eraId: undefined,
branch: undefined,
keywords: ''
});
// 新增
const add = () => {
emit('add');
};
const handleSearch = () => {
emit('search', where);
}
/* 重置 */
const reset = () => {
resetFields();
handleSearch();
};
const onGrade = (gradeId: number) => {
where.gradeId = gradeId;
handleSearch();
}
const reload = () => {
listBszxGrade({}).then(res => {
gradeList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
}
reload();
watch(
() => props.selection,
() => {
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,244 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<a-button
type="text"
@click="openUrl('/bszx/grade')"
>年级设置
</a-button
>
<a-button
type="text"
@click="openUrl('/bszx/class')"
>班级设置
</a-button
>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'era'">
<span>{{ record.era }}</span>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxClassEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxClassEdit from './components/bszxClassEdit.vue';
import {pageBszxClass, removeBszxClass, removeBatchBszxClass} from '@/api/bszx/bszxClass';
import type {BszxClass, BszxClassParam} from '@/api/bszx/bszxClass/model';
import {getPageTitle, openUrl} from "@/utils/common";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxClass[]>([]);
// 当前编辑数据
const current = ref<BszxClass | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxClass({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 120,
},
{
title: '初高中',
dataIndex: 'branch',
key: 'branch',
width: 120,
customRender: ({ text }) => ['','初中','高中'][text]
},
{
title: '年级',
dataIndex: 'gradeName',
key: 'gradeName',
width: 120
},
// {
// title: '年级',
// dataIndex: 'gradeId',
// key: 'gradeId',
// width: 120
// },
{
title: '班级',
dataIndex: 'name',
key: 'name'
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: BszxClassParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxClass) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxClass) => {
const hide = message.loading('请求中..', 0);
removeBszxClass(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxClass(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxClass) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxClass'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,170 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑年级' : '添加年级'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="分部" name="branch">
<a-radio-group v-model:value="form.branch">
<a-radio-button :value="1">初中部</a-radio-button>
<a-radio-button :value="2">高中部</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item label="年级" name="name">
<a-input
allow-clear
placeholder="请输入年级"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxGrade, updateBszxGrade } from '@/api/bszx/bszxGrade';
import { BszxGrade } from '@/api/bszx/bszxGrade/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxGrade | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxGrade>({
id: undefined,
branch: 2,
name: undefined,
comments: undefined,
status: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxGradeName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-年级名称',
trigger: 'blur'
}
]
});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxGrade : addBszxGrade;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,56 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
<a-radio-group v-model:value="where.branch" @change="handleSearch">
<a-radio-button :value="1">初中部</a-radio-button>
<a-radio-button :value="2">高中部</a-radio-button>
</a-radio-group>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import useSearch from "@/utils/use-search";
import { watch } from 'vue';
import {BszxGradeParam} from "@/api/bszx/bszxGrade/model";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: BszxGradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where} = useSearch<BszxGradeParam>({
branch: undefined,
keywords: ''
});
// 新增
const add = () => {
emit('add');
};
const handleSearch = () => {
emit('search', where);
}
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,237 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<a-button
type="text"
@click="openUrl('/bszx/grade')"
>年级设置
</a-button
>
<a-button
type="text"
@click="openUrl('/bszx/class')"
>班级设置
</a-button
>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="appBszxGradeId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxGradeEdit v-model:visible="showEdit" :data="current" @done="reload" />
</a-page-header>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxGradeEdit from './components/bszxGradeEdit.vue';
import { pageBszxGrade, removeBszxGrade, removeBatchBszxGrade } from '@/api/bszx/bszxGrade';
import type { BszxGrade, BszxGradeParam } from '@/api/bszx/bszxGrade/model';
import {getPageTitle, openUrl} from "@/utils/common";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxGrade[]>([]);
// 当前编辑数据
const current = ref<BszxGrade | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxGrade({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
// {
// title: 'ID',
// dataIndex: 'id',
// key: 'id',
// align: 'center',
// width: 90,
// },
{
title: '初高中',
dataIndex: 'branch',
key: 'branch',
width: 120,
customRender: ({ text }) => ['','初中','高中'][text]
},
{
title: '年级',
dataIndex: 'name',
key: 'name'
},
{
title: '排序',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
width: 120,
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: BszxGradeParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxGrade) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxGrade) => {
const hide = message.loading('请求中..', 0);
removeBszxGrade(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxGrade(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxGrade) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxGrade'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,249 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑捐款记录' : '添加捐款记录'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="年龄" name="age">
<a-input
allow-clear
placeholder="请输入年龄"
v-model:value="form.age"
/>
</a-form-item>
<a-form-item label="姓名" name="name">
<a-input
allow-clear
placeholder="请输入姓名"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="性别 1男 2女" name="sex">
<a-input
allow-clear
placeholder="请输入性别 1男 2女"
v-model:value="form.sex"
/>
</a-form-item>
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="班级" name="className">
<a-input
allow-clear
placeholder="请输入班级"
v-model:value="form.className"
/>
</a-form-item>
<a-form-item label="年级" name="gradeName">
<a-input
allow-clear
placeholder="请输入年级"
v-model:value="form.gradeName"
/>
</a-form-item>
<a-form-item label="居住地址" name="address">
<a-input
allow-clear
placeholder="请输入居住地址"
v-model:value="form.address"
/>
</a-form-item>
<a-form-item label="工作单位" name="workUnit">
<a-input
allow-clear
placeholder="请输入工作单位"
v-model:value="form.workUnit"
/>
</a-form-item>
<a-form-item label="职务" name="position">
<a-input
allow-clear
placeholder="请输入职务"
v-model:value="form.position"
/>
</a-form-item>
<a-form-item label="捐赠证书" name="certificate">
<a-input
allow-clear
placeholder="请输入捐赠证书"
v-model:value="form.certificate"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPay, updateBszxPay } from '@/api/bszx/bszxPay';
import { BszxPay } from '@/api/bszx/bszxPay/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPay | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPay>({
id: undefined,
age: undefined,
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
number: undefined,
extra: undefined,
dateTime: undefined,
certificate: undefined,
formData: undefined,
formId: undefined,
userId: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxPayName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-捐款记录名称',
trigger: 'blur'
}
]
});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPay : addBszxPay;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,566 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
:width="`80%`"
:visible="visible"
:confirm-loading="loading"
:maxable="maxAble"
:title="isUpdate ? '编辑订单' : '订单详情'"
:body-style="{ paddingBottom: '8px', background: '#f3f3f3' }"
@update:visible="updateVisible"
:maskClosable="false"
:footer="null"
@ok="save"
>
<a-card style="margin-bottom: 20px" :bordered="false">
<a-descriptions :column="3">
<!-- 第一排-->
<a-descriptions-item
label="订单编号"
span="3"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<span @click="copyText(form.orderNo)">{{ form.orderNo }}</span>
</a-descriptions-item>
<!-- 第二排-->
<a-descriptions-item
label="买家信息"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-space class="flex items-center">
<a-avatar :src="form.avatar" size="small"/>
{{ form.realName }}
</a-space>
</a-descriptions-item>
<a-descriptions-item
label="订单金额"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.totalPrice }}
</a-descriptions-item>
<a-descriptions-item
label="订单状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.orderStatus === 0">未完成</a-tag>
<a-tag v-if="form.orderStatus === 1" color="green">已完成</a-tag>
<a-tag v-if="form.orderStatus === 2" color="red">已取消</a-tag>
<a-tag v-if="form.orderStatus === 3" color="red">取消中</a-tag>
<a-tag v-if="form.orderStatus === 4" color="red">退款申请中</a-tag>
<a-tag v-if="form.orderStatus === 5" color="red">退款被拒绝</a-tag>
<a-tag v-if="form.orderStatus === 6" color="orange">退款成功</a-tag>
<a-tag v-if="form.orderStatus === 7" color="pink">客户端申请退款</a-tag>
</a-descriptions-item>
<!-- 第三排-->
<a-descriptions-item
label="手机号码"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.phone }}
</a-descriptions-item>
<a-descriptions-item
label="实付金额"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.payPrice }}
</a-descriptions-item>
<a-descriptions-item
label="支付状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.payStatus == 1" color="green">已付款</a-tag>
<a-tag v-if="form.payStatus == 0">未付款</a-tag>
<a-tag v-if="form.payStatus == 3">未付款,占场中</a-tag>
</a-descriptions-item>
<!-- 第四排-->
<a-descriptions-item
label="收货地址"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.address }}
</a-descriptions-item>
<a-descriptions-item
label="减少的金额"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.reducePrice }}
</a-descriptions-item>
<a-descriptions-item
label="核销状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.deliveryStatus == 10">未核销</a-tag>
<a-tag v-if="form.deliveryStatus == 20" color="green">已核销</a-tag>
<a-tag v-if="form.deliveryStatus == 30" color="bule">部分核销</a-tag>
</a-descriptions-item>
<!-- 第五排-->
<a-descriptions-item
label="备注信息"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ form.comments }}
</a-descriptions-item>
<a-descriptions-item
label="支付方式"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tooltip :title="`支付时间:${form.payTime || ''}`">
<template v-if="form.payStatus == 1">
<a-tag v-if="form.payType == 0">余额支付</a-tag>
<a-tag v-if="form.payType == 1">
<WechatOutlined class="tag-icon"/>
微信支付
</a-tag>
<a-tag v-if="form.payType == 2">积分</a-tag>
<a-tag v-if="form.payType == 3">
<AlipayCircleOutlined class="tag-icon"/>
支付宝
</a-tag>
<a-tag v-if="form.payType == 4">
<IdcardOutlined class="tag-icon"/>
现金
</a-tag>
<a-tag v-if="form.payType == 5">
<IdcardOutlined class="tag-icon"/>
POS机
</a-tag>
<a-tag v-if="form.payType == 6">
<IdcardOutlined class="tag-icon"/>
VIP月卡
</a-tag>
<a-tag v-if="form.payType == 7">
<IdcardOutlined class="tag-icon"/>
VIP年卡
</a-tag>
<a-tag v-if="form.payType == 8">
<IdcardOutlined class="tag-icon"/>
VIP次卡
</a-tag>
<a-tag v-if="form.payType == 9">
<IdcardOutlined class="tag-icon"/>
IC月卡
</a-tag>
<a-tag v-if="form.payType == 10">
<IdcardOutlined class="tag-icon"/>
IC年卡
</a-tag>
<a-tag v-if="form.payType == 11">
<IdcardOutlined class="tag-icon"/>
IC次卡
</a-tag>
<a-tag v-if="form.payType == 12">
<IdcardOutlined class="tag-icon"/>
免费
</a-tag>
<a-tag v-if="form.payType == 13">
<IdcardOutlined class="tag-icon"/>
VIP充值卡
</a-tag>
<a-tag v-if="form.payType == 14">
<IdcardOutlined class="tag-icon"/>
IC充值卡
</a-tag>
<a-tag v-if="form.payType == 15">
<IdcardOutlined class="tag-icon"/>
积分支付
</a-tag>
<a-tag v-if="form.payType == 16">
<IdcardOutlined class="tag-icon"/>
VIP季卡
</a-tag>
<a-tag v-if="form.payType == 17">
<IdcardOutlined class="tag-icon"/>
IC季卡
</a-tag>
</template>
</a-tooltip>
</a-descriptions-item>
<a-descriptions-item
label="开票状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.isInvoice == 0">未开具</a-tag>
<a-tag v-if="form.isInvoice == 1" color="green">已开具</a-tag>
<a-tag v-if="form.isInvoice == 2" color="blue">不能开具</a-tag>
</a-descriptions-item>
<!-- 第六排-->
<a-descriptions-item
label="下单时间"
:labelStyle="{ width: '90px', color: '#808080' }"
>
{{ toDateString(form.createTime, 'yyyy-MM-dd HH:mm') }}
</a-descriptions-item>
<a-descriptions-item
label="交易流水号"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tooltip :title="form.payTime">{{ form.transactionId }}</a-tooltip>
</a-descriptions-item>
<a-descriptions-item
label="结算状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.isSettled == 0">未结算</a-tag>
<a-tag v-if="form.isSettled == 1" color="green">已结算</a-tag>
</a-descriptions-item>
<!-- <a-descriptions-item span="3">-->
<!-- <a-divider/>-->
<!-- </a-descriptions-item>-->
</a-descriptions>
</a-card>
<a-card class="order-card" :bordered="false">
<a-spin :spinning="loading">
<a-table
:data-source="orderInfo"
:columns="columns"
:pagination="false"
/>
</a-spin>
</a-card>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref, reactive, watch} from 'vue';
import {Form} from 'ant-design-vue';
import {assignObject} from 'ele-admin-pro';
import {ColumnItem} from 'ele-admin-pro/es/ele-pro-table/types';
import {
CheckOutlined,
CloseOutlined,
CoffeeOutlined
} from '@ant-design/icons-vue';
import {ShopOrder} from '@/api/shop/shopOrder/model';
import {BszxPay} from '@/api/bszx/bszxPay/model';
import {pageBszxPay} from '@/api/bszx/bszxPay';
import {toDateString} from 'ele-admin-pro';
import {copyText} from "@/utils/common";
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: ShopOrder | null;
}>();
export interface step {
title?: String | undefined;
subTitle?: String | undefined;
description?: String | undefined;
}
// 是否是修改
const isUpdate = ref(false);
// 是否显示最大化切换按钮
const maxAble = ref(true);
// 订单信息
const orderInfo = ref<BszxPay[]>([]);
// 步骤条
const steps = ref<step[]>([
{
title: '报餐',
description: undefined
},
{
title: '付款',
description: undefined
},
{
title: '发餐',
description: undefined
},
{
title: '取餐',
description: undefined
},
{
title: '完成',
description: undefined
}
]);
const active = ref(2);
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 订单信息
const form = reactive<ShopOrder>({
// 订单号
orderId: undefined,
// 订单编号
orderNo: undefined,
// 订单类型0商城订单 1预定订单/外卖 2会员卡
type: undefined,
// 快递/自提
deliveryType: undefined,
// 下单渠道0小程序预定 1俱乐部训练场 3活动订场
channel: undefined,
// 微信支付订单号
transactionId: undefined,
// 微信退款订单号
refundOrder: undefined,
// 商户ID
merchantId: undefined,
// 商户名称
merchantName: undefined,
// 商户编号
merchantCode: undefined,
// 使用的优惠券id
couponId: undefined,
// 使用的会员卡id
cardId: undefined,
// 关联管理员id
adminId: undefined,
// 核销管理员id
confirmId: undefined,
// IC卡号
icCard: undefined,
// 头像
avatar: undefined,
// 真实姓名
realName: undefined,
// 手机号码
phone: undefined,
// 收货地址
address: undefined,
//
addressLat: undefined,
//
addressLng: undefined,
// 自提店铺id
selfTakeMerchantId: undefined,
// 自提店铺
selfTakeMerchantName: undefined,
// 配送开始时间
sendStartTime: undefined,
// 配送结束时间
sendEndTime: undefined,
// 发货店铺id
expressMerchantId: undefined,
// 发货店铺
expressMerchantName: undefined,
// 订单总额
totalPrice: undefined,
// 减少的金额使用VIP会员折扣、优惠券抵扣、优惠券折扣后减去的价格
reducePrice: undefined,
// 实际付款
payPrice: undefined,
// 用于统计
price: undefined,
// 价钱,用于积分赠送
money: undefined,
// 退款金额
refundMoney: undefined,
// 教练价格
coachPrice: undefined,
// 购买数量
totalNum: undefined,
// 教练id
coachId: undefined,
// 支付的用户id
payUserId: undefined,
// 0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
payType: undefined,
// 代付支付方式,0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
friendPayType: undefined,
// 0未付款1已付款
payStatus: undefined,
// 0未使用1已完成2已取消3取消中4退款申请中5退款被拒绝6退款成功7客户端申请退款
orderStatus: undefined,
// 发货状态(10未发货 20已发货 30部分发货)
deliveryStatus: undefined,
// 发货时间
deliveryTime: undefined,
// 优惠类型0无、1抵扣优惠券、2折扣优惠券、3、VIP月卡、4VIP年卡5VIP次卡、6VIP会员卡、7IC月卡、8IC年卡、9IC次卡、10IC会员卡、11免费订单、12VIP充值卡、13IC充值卡、14VIP季卡、15IC季卡
couponType: undefined,
// 优惠说明
couponDesc: undefined,
// 二维码地址,保存订单号,支付成功后才生成
qrcode: undefined,
// vip月卡年卡、ic月卡年卡回退次数
returnNum: undefined,
// vip充值回退金额
returnMoney: undefined,
// 预约详情开始时间数组
startTime: undefined,
// 是否已开具发票0未开发票1已开发票2不能开具发票
isInvoice: undefined,
// 发票流水号
invoiceNo: undefined,
// 支付时间
payTime: undefined,
// 退款时间
refundTime: undefined,
// 申请退款时间
refundApplyTime: undefined,
// 过期时间
expirationTime: undefined,
// 对账情况0=未对账1=已对账3=已对账金额对不上4=未查询到该订单
checkBill: undefined,
// 订单是否已结算(0未结算 1已结算)
isSettled: undefined,
// 系统版本号 0当前版本 value=其他版本
version: undefined,
// 用户id
userId: undefined,
// 备注
comments: undefined,
// 排序号
sortNumber: undefined,
// 是否删除, 0否, 1是
deleted: undefined,
// 租户id
tenantId: undefined,
// 修改时间
updateTime: undefined,
// 创建时间
createTime: undefined,
// 自提码
selfTakeCode: undefined,
// 是否已收到赠品
hasTakeGift: undefined,
});
// 请求状态
const loading = ref(true);
const {resetFields} = useForm(form);
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const columns = ref<ColumnItem[]>([
{
title: '商品名称',
dataIndex: 'formName',
key: 'formName',
align: 'center',
width: 280
},
{
title: '金额',
dataIndex: 'price',
align: 'center'
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center'
},
{
title: '操作',
dataIndex: 'isFree',
align: 'center'
}
]);
/* 制作步骤条 */
const loadSteps = (order) => {
steps.value = [];
steps.value.push({
title: '下单'
});
steps.value.push({
title: '付款'
});
steps.value.push({
title: '发货'
});
steps.value.push({
title: '收货'
});
steps.value.push({
title: '完成'
});
// 下单
if (order.payStatus == 10) {
active.value = 0;
steps.value[0].description = order.createTime;
}
// 付款
if (order.payStatus == 20) {
active.value = 1;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
}
// 发货
if (order.payStatus == 20 && order.deliveryStatus == 20) {
active.value = 2;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
steps.value[2].description = order.deliveryTime;
}
// 收货
if (order.payStatus == 20 && order.receiptStatus == 20) {
active.value = 3;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
steps.value[2].description = order.deliveryTime;
steps.value[3].description = order.receiptTime;
}
// 完成
if (order.payStatus == 20 && order.orderStatus == 30) {
active.value = 4;
steps.value[0].description = order.createTime;
steps.value[1].description = order.payTime;
steps.value[2].description = order.deliveryTime;
steps.value[3].description = order.receiptTime;
}
// 已取消
if (order.orderStatus == 20) {
active.value = 4;
}
};
// const getOrderInfo = () => {
// const orderId = props.data?.orderId;
// listOrderInfo({ orderId }).then((data) => {
// orderInfo.value = data.filter((d) => d.totalNum > 0);
// });
// };
/* 保存编辑 */
const save = () => {
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = true;
assignObject(form, props.data);
pageBszxPay({orderNo: form.orderNo}).then((res) => {
if (res?.list) {
orderInfo.value = res?.list;
}
loading.value = false;
});
loadSteps(props.data);
}
} else {
resetFields();
}
}
);
</script>
<script lang="ts">
import * as MenuIcons from '@/layout/menu-icons';
export default {
name: 'BszxOrderInfo',
components: MenuIcons
};
</script>

View File

@@ -0,0 +1,204 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-select
v-model:value="where.payStatus"
style="width: 150px"
placeholder="付款状态"
@change="search"
>
<a-select-option value="">全部</a-select-option>
<a-select-option :value="1">已付款</a-select-option>
<a-select-option :value="0">未付款</a-select-option>
</a-select>
<a-select
v-model:value="where.orderStatus"
style="width: 150px"
placeholder="订单状态"
@change="search"
>
<a-select-option value="">全部</a-select-option>
<a-select-option :value="1">已完成</a-select-option>
<a-select-option :value="0">未完成</a-select-option>
<a-select-option :value="2">未使用</a-select-option>
<a-select-option :value="3">已取消</a-select-option>
<a-select-option :value="4">退款中</a-select-option>
<a-select-option :value="5">退款被拒</a-select-option>
<a-select-option :value="6">退款成功</a-select-option>
</a-select>
<a-select
:options="getPayType()"
v-model:value="where.payType"
style="width: 150px"
placeholder="付款方式"
@change="search"
/>
<a-select
v-model:value="where.isInvoice"
style="width: 150px"
placeholder="开票状态"
@change="search"
>
<a-select-option :value="1">已开票</a-select-option>
<a-select-option :value="0">未开票</a-select-option>
<a-select-option :value="2">不能开票</a-select-option>
</a-select>
<a-range-picker
v-model:value="dateRange"
@change="search"
value-format="YYYY-MM-DD"
/>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 280px"
v-model:value="where.keywords"
@search="reload"
/>
<a-button @click="reset">重置</a-button>
<a-button @click="handleExport">导出</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { utils, writeFile } from 'xlsx';
import { message } from 'ant-design-vue';
import useSearch from "@/utils/use-search";
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {listShopOrder} from "@/api/shop/shopOrder";
import {getPayType} from "@/utils/shop";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: ShopOrderParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const { where, resetFields } = useSearch<ShopOrderParam>({
keywords: '',
orderId: undefined,
orderNo: undefined,
createTimeStart: undefined,
createTimeEnd: undefined,
userId: undefined,
phone: undefined,
payStatus: undefined,
orderStatus: undefined,
payType: undefined,
isInvoice: undefined,
});
const reload = () => {
emit('search', where);
};
/* 搜索 */
const search = () => {
const [d1, d2] = dateRange.value ?? [];
xlsFileName.value = `${d1}${d2}`;
where.createTimeStart = d1 ? d1 + ' 00:00:00' : undefined;
where.createTimeEnd = d2 ? d2 + ' 23:59:59' : undefined;
emit('search', {
...where
});
};
/* 重置 */
const reset = () => {
resetFields();
search();
};
const dateRange = ref<[string, string]>(['', '']);
// 变量
const loading = ref(false);
const orders = ref<ShopOrder[]>([])
const xlsFileName = ref<string>();
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'订单编号',
'订单标题',
'买家姓名',
'手机号码',
'实付金额(元)',
'支付方式',
'付款时间',
'下单时间'
]
];
await listShopOrder(where)
.then((list) => {
orders.value = list;
list?.forEach((d: ShopOrder) => {
array.push([
`${d.orderNo}`,
`${d.comments}`,
`${d.realName}`,
`${d.phone}`,
`${d.payPrice}`,
`${getPayType(d.payType)}`,
`${d.payTime || ''}`,
`${d.createTime}`
]);
});
const sheetName = `订单数据`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{ wch: 10 },
{ wch: 40 },
{ wch: 20 },
{ wch: 20 },
{ wch: 60 },
{ wch: 15 },
{ wch: 10 },
{ wch: 10 },
{ wch: 20 },
{ wch: 10 },
{ wch: 20 }
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {});
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,307 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="orderId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div @click="onSearch(record)" class="cursor-pointer">{{ record.name || '匿名' }}</div>
</template>
<template v-if="column.key === 'phone'">
<div v-if="record.mobile" class="text-gray-400">{{ record.mobile }}</div>
<div v-else class="text-gray-600">{{ record.phone }}</div>
</template>
<template v-if="column.key === 'payType'">
<template v-for="item in getPayType()">
<template v-if="record.payStatus == 1">
<span v-if="item.value == record.payType">{{ item.label }}</span>
</template>
<template v-else>
<span></span>
</template>
</template>
</template>
<template v-if="column.key === 'payStatus'">
<a-tag v-if="record.payStatus == 1" color="green" @click="updatePayStatus(record)">已付款</a-tag>
<a-tag v-if="record.payStatus == 0" @click="updatePayStatus(record)">未付款</a-tag>
<a-tag v-if="record.payStatus == 3">未付款,占场中</a-tag>
</template>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'sex'">
<a-tag v-if="record.sex === 1"></a-tag>
<a-tag v-if="record.sex === 2"></a-tag>
</template>
<template v-if="column.key === 'deliveryStatus'">
<a-tag v-if="record.deliveryStatus == 10">未核销</a-tag>
<a-tag v-if="record.deliveryStatus == 20" color="green">已核销</a-tag>
<a-tag v-if="record.deliveryStatus == 30" color="bule">部分核销</a-tag>
</template>
<template v-if="column.key === 'orderStatus'">
<a-tag v-if="record.orderStatus === 0">未完成</a-tag>
<a-tag v-if="record.orderStatus === 1" color="green">已完成</a-tag>
<a-tag v-if="record.orderStatus === 2" color="red">已取消</a-tag>
<a-tag v-if="record.orderStatus === 3" color="red">取消中</a-tag>
<a-tag v-if="record.orderStatus === 4" color="red">退款申请中</a-tag>
<a-tag v-if="record.orderStatus === 5" color="red">退款被拒绝</a-tag>
<a-tag v-if="record.orderStatus === 6" color="orange">退款成功</a-tag>
<a-tag v-if="record.orderStatus === 7" color="pink">客户端申请退款</a-tag>
</template>
<template v-if="column.key === 'isInvoice'">
<a-tag v-if="record.isInvoice == 0">未开具</a-tag>
<a-tag v-if="record.isInvoice == 1" color="green">已开具</a-tag>
<a-tag v-if="record.isInvoice == 2" color="blue">不能开具</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<OrderInfo v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bszx/extra.vue";
import {pageBszxOrder} from "@/api/bszx/bszxOrder";
import OrderInfo from './components/orderInfo.vue';
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {updateShopOrder} from "@/api/shop/shopOrder";
import {message} from "ant-design-vue";
import {updateUser} from "@/api/system/user";
import {getPayType} from '@/utils/shop';
import {repairOrder} from "@/api/bszx/bszxPay";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<ShopOrder[]>([]);
// 当前编辑数据
const current = ref<ShopOrder | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxOrder({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '订单号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center',
width: 200,
},
{
title: '姓名',
dataIndex: 'realName',
key: 'realName',
align: 'center'
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center'
},
{
title: '实付金额',
dataIndex: 'payPrice',
key: 'payPrice',
align: 'center',
customRender: ({text}) => '¥' + text
},
{
title: '支付方式',
dataIndex: 'payType',
key: 'payType',
align: 'center'
},
{
title: '支付状态',
dataIndex: 'payStatus',
key: 'payStatus',
align: 'center'
},
{
title: '核销状态',
dataIndex: 'deliveryStatus',
key: 'deliveryStatus',
align: 'center',
},
{
title: '开票状态',
dataIndex: 'isInvoice',
key: 'isInvoice',
align: 'center',
},
{
title: '订单状态',
dataIndex: 'orderStatus',
key: 'orderStatus',
align: 'center',
},
// {
// title: '支付时间',
// dataIndex: 'payTime',
// key: 'payTime',
// align: 'center',
// width: 180,
// sorter: true,
// ellipsis: true
// },
{
title: '下单时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true,
ellipsis: true
},
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: ShopOrderParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
const onSearch = (item: ShopOrder) => {
reload({userId: item.userId})
}
/* 打开编辑弹窗 */
const openEdit = (row?: ShopOrder) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/**
* 修复订单支付状态
*/
const updatePayStatus = (record: ShopOrder) => {
// 修复订单
repairOrder(record).then(() => {
message.success('修复成功');
}).then(() => {
if(record.realName == '' || record.realName == undefined){
// 更新用户真实姓名
updateUser({
userId: record.userId,
realName: record.realName
})
}
reload();
})
}
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: ShopOrder) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
import * as MenuIcons from '@/layout/menu-icons';
export default {
name: 'BszxOrder',
components: MenuIcons
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,249 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑捐款记录' : '添加捐款记录'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="年龄" name="age">
<a-input
allow-clear
placeholder="请输入年龄"
v-model:value="form.age"
/>
</a-form-item>
<a-form-item label="姓名" name="name">
<a-input
allow-clear
placeholder="请输入姓名"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="性别 1男 2女" name="sex">
<a-input
allow-clear
placeholder="请输入性别 1男 2女"
v-model:value="form.sex"
/>
</a-form-item>
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="班级" name="className">
<a-input
allow-clear
placeholder="请输入班级"
v-model:value="form.className"
/>
</a-form-item>
<a-form-item label="年级" name="gradeName">
<a-input
allow-clear
placeholder="请输入年级"
v-model:value="form.gradeName"
/>
</a-form-item>
<a-form-item label="居住地址" name="address">
<a-input
allow-clear
placeholder="请输入居住地址"
v-model:value="form.address"
/>
</a-form-item>
<a-form-item label="工作单位" name="workUnit">
<a-input
allow-clear
placeholder="请输入工作单位"
v-model:value="form.workUnit"
/>
</a-form-item>
<a-form-item label="职务" name="position">
<a-input
allow-clear
placeholder="请输入职务"
v-model:value="form.position"
/>
</a-form-item>
<a-form-item label="捐赠证书" name="certificate">
<a-input
allow-clear
placeholder="请输入捐赠证书"
v-model:value="form.certificate"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPay, updateBszxPay } from '@/api/bszx/bszxPay';
import { BszxPay } from '@/api/bszx/bszxPay/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPay | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPay>({
id: undefined,
age: undefined,
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
number: undefined,
extra: undefined,
dateTime: undefined,
certificate: undefined,
formData: undefined,
formId: undefined,
userId: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxPayName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-捐款记录名称',
trigger: 'blur'
}
]
});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPay : addBszxPay;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,235 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-radio-group v-model:value="branchId" @change="onBranchId">
<a-radio-button :value="1">初中部</a-radio-button>
<a-radio-button :value="2">高中部</a-radio-button>
</a-radio-group>
<a-select
v-if="branchId"
show-search
v-model:value="where.gradeName"
style="width: 240px"
placeholder="选择年级"
:options="gradeList"
@change="onGrade"
></a-select>
<a-select
v-if="where.gradeName"
show-search
v-model:value="where.className"
style="width: 240px"
placeholder="选择年级"
:options="classList"
@change="onClass"
></a-select>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 220px"
v-model:value="where.keywords"
@search="reload"
/>
<a-range-picker
v-model:value="dateRange"
@change="search"
value-format="YYYY-MM-DD"
/>
<a-button @click="handleExport">导出</a-button>
</a-space>
</template>
<script lang="ts" setup>
import {ref, watch} from 'vue';
import {utils, writeFile} from 'xlsx';
import dayjs from 'dayjs';
import {message} from 'ant-design-vue';
import useSearch from "@/utils/use-search";
import {BszxPayParam} from "@/api/bszx/bszxPay/model";
import {BszxBm} from "@/api/bszx/bszxBm/model";
import {listBszxPay} from "@/api/bszx/bszxPay";
import {BszxClassParam} from "@/api/bszx/bszxClass/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
import {listBszxClass} from "@/api/bszx/bszxClass";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: BszxPayParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where} = useSearch<BszxPayParam>({
id: undefined,
keywords: '',
gradeName: undefined,
className: undefined,
createTimeStart: undefined,
createTimeEnd: undefined,
userId: undefined
});
const reload = () => {
emit('search', where);
};
/* 搜索 */
const search = () => {
const [d1, d2] = dateRange.value ?? [];
where.createTimeStart = d1 ? d1 + ' 00:00:00' : undefined;
where.createTimeEnd = d2 ? d2 + ' 23:59:59' : undefined;
emit('search', {
...where
});
};
const dateRange = ref<[string, string]>(['', '']);
// 变量
const loading = ref(false);
const bmList = ref<BszxBm[]>([])
const xlsFileName = ref<string>();
const branchId = ref<number>();
const gradeId = ref<number>();
const gradeList = ref<BszxClassParam[]>([]);
const classList = ref<BszxClassParam[]>([]);
const getGradeList = () => {
listBszxGrade({branch: branchId.value}).then(res => {
gradeList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
};
const getClassList = () => {
listBszxClass({gradeId: gradeId.value}).then(res => {
classList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
}
const onGrade = (gradeName: number, item: any) => {
where.gradeName = item.name;
if (gradeName) {
console.log(item)
gradeId.value = item.id;
getClassList();
}
emit('search', where);
}
const onBranchId = () => {
getGradeList();
}
const onClass = (classId, item) => {
console.log(classId);
where.className = item.name;
reload();
}
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'订单编号',
'姓名',
'手机号码',
'捐款金额',
'性别',
'年级',
'班级',
'居住地址',
'工作单位',
'职务',
'捐款时间'
]
];
// 按搜索结果导出
where.sceneType = 'Content';
await listBszxPay(where)
.then((list) => {
bmList.value = list;
list?.forEach((d: BszxBm) => {
array.push([
`${d.orderNo}`,
`${d.name}`,
`${d.mobile}`,
`${d.price}`,
`${d.sex == 1 ? '男' : ''}${d.sex == 2 ? '女' : '-'}`,
`${d.gradeName ? d.gradeName : '-'}`,
`${d.className ? d.className : '-'}`,
`${d.address ? d.address : '-'}`,
`${d.workUnit ? d.workUnit : '-'}`,
`${d.position ? d.position : '-'}`,
`${d.createTime}`
]);
});
const sheetName = `导出捐款记录${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10},
{wch: 40},
{wch: 20},
{wch: 20},
{wch: 60},
{wch: 15},
{wch: 10},
{wch: 10},
{wch: 20},
{wch: 10},
{wch: 20}
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {
});
};
getGradeList();
watch(
() => props.totalPriceAmount,
(totalPriceAmount) => {
console.log(totalPriceAmount, 'totalPriceAmount')
}
);
</script>

View File

@@ -0,0 +1,336 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:parse-data="parseData"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div @click="onSearch(record)" class="cursor-pointer">{{ record.name || '匿名' }}</div>
</template>
<template v-if="column.key === 'phone'">
<div v-if="record.mobile" class="text-gray-400">{{ record.mobile }}</div>
<div v-else class="text-gray-600">{{ record.phone }}</div>
</template>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'sex'">
<a-tag v-if="record.sex === 1"></a-tag>
<a-tag v-if="record.sex === 2"></a-tag>
</template>
<template v-if="column.key === 'present'">
<a-tag v-if="record.present"></a-tag>
<a-tag v-else>不能</a-tag>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
<template #footer>
<span v-if="totalPriceAmount" class="text-red-500 font-bold">小计{{ totalPriceAmount.toFixed(2) }}</span>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxPayEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxPayEdit from './components/bszxPayEdit.vue';
import {pageBszxPay, removeBszxPay, removeBatchBszxPay} from '@/api/bszx/bszxPay';
import type {BszxPay, BszxPayParam} from '@/api/bszx/bszxPay/model';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bszx/extra.vue";
import {PageResult} from "@/api";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxPay[]>([]);
// 当前编辑数据
const current = ref<BszxPay | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
const totalPriceAmount = ref<number>(0);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageBszxPay({
...where,
...orders,
page,
limit
});
};
// 整理数据
const parseData = (data: PageResult<BszxPay>) => {
totalPriceAmount.value = 0;
data.list?.map((item) => {
if(item.price){
totalPriceAmount.value += Number(item.price)
}
})
return data;
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '订单编号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center',
width: 200,
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '手机号码',
dataIndex: 'phone',
key: 'phone',
align: 'center',
width: 120
},
{
title: '捐款金额',
dataIndex: 'price',
key: 'price',
width: 120,
align: 'center',
sorter: true,
customRender: ({text}) => '¥' + text
},
{
title: '性别',
dataIndex: 'sex',
key: 'sex',
align: 'center',
customRender: ({text}) => ['', '男', '女'][text]
},
{
title: '分部',
dataIndex: 'branchName',
key: 'branchName',
align: 'center',
},
{
title: '年级',
dataIndex: 'gradeName',
key: 'gradeName',
align: 'center',
},
{
title: '班级',
dataIndex: 'className',
key: 'className',
align: 'center',
},
{
title: '居住地址',
dataIndex: 'address',
key: 'address',
align: 'center',
},
{
title: '工作单位',
dataIndex: 'workUnit',
key: 'workUnit',
align: 'center',
},
{
title: '职务',
dataIndex: 'position',
key: 'position',
align: 'center',
},
// {
// title: '捐赠证书',
// dataIndex: 'certificate',
// key: 'certificate',
// align: 'center',
// },
{
title: '心愿',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
// {
// title: '状态',
// dataIndex: 'status',
// key: 'status',
// align: 'center',
// },
{
title: '捐款时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true,
ellipsis: true
},
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: BszxPayParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
const onSearch = (item: BszxPay) => {
reload({userId: item.userId})
}
/* 打开编辑弹窗 */
const openEdit = (row?: BszxPay) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxPay) => {
const hide = message.loading('请求中..', 0);
removeBszxPay(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxPay(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxPay) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
// openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxPay'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,270 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑应用-百色中学-捐款记录' : '添加应用-百色中学-捐款记录'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="年龄" name="age">
<a-input
allow-clear
placeholder="请输入年龄"
v-model:value="form.age"
/>
</a-form-item>
<a-form-item label="姓名" name="name">
<a-input
allow-clear
placeholder="请输入姓名"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="性别 1男 2女" name="sex">
<a-input
allow-clear
placeholder="请输入性别 1男 2女"
v-model:value="form.sex"
/>
</a-form-item>
<a-form-item label="手机号码" name="phone">
<a-input
allow-clear
placeholder="请输入手机号码"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="班级" name="className">
<a-input
allow-clear
placeholder="请输入班级"
v-model:value="form.className"
/>
</a-form-item>
<a-form-item label="年级" name="gradeName">
<a-input
allow-clear
placeholder="请输入年级"
v-model:value="form.gradeName"
/>
</a-form-item>
<a-form-item label="居住地址" name="address">
<a-input
allow-clear
placeholder="请输入居住地址"
v-model:value="form.address"
/>
</a-form-item>
<a-form-item label="工作单位" name="workUnit">
<a-input
allow-clear
placeholder="请输入工作单位"
v-model:value="form.workUnit"
/>
</a-form-item>
<a-form-item label="职务" name="position">
<a-input
allow-clear
placeholder="请输入职务"
v-model:value="form.position"
/>
</a-form-item>
<a-form-item label="捐赠证书" name="certificate">
<a-input
allow-clear
placeholder="请输入捐赠证书"
v-model:value="form.certificate"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPay, updateBszxPay } from '@/api/bszx/bszxPay';
import { BszxPay } from '@/api/bszx/bszxPay/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPay | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPay>({
id: undefined,
age: undefined,
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
number: undefined,
extra: undefined,
dateTime: undefined,
certificate: undefined,
formData: undefined,
formId: undefined,
userId: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
appBszxPayName: [
{
required: true,
type: 'string',
message: '请填写应用-百色中学-捐款记录名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPay : addBszxPay;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,141 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 280px"
v-model:value="where.keywords"
@search="reload"
/>
<a-button type="text" @click="handleExport">导出</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { utils, writeFile } from 'xlsx';
import dayjs from 'dayjs';
import { message } from 'ant-design-vue';
import useSearch from "@/utils/use-search";
import {BszxPayParam} from "@/api/bszx/bszxPay/model";
import {BszxBm} from "@/api/bszx/bszxBm/model";
import {listBszxPay} from "@/api/bszx/bszxPay";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: BszxPayParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const { where } = useSearch<BszxPayParam>({
id: undefined,
keywords: '',
userId: undefined
});
const reload = () => {
emit('search', where);
};
// 变量
const loading = ref(false);
const bmList = ref<BszxBm[]>([])
const xlsFileName = ref<string>();
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'用户ID',
'姓名',
'性别',
'手机号码',
'班级',
'年级',
'居住地址',
'工作单位',
'职务',
'是否能到场',
'邀请函',
'捐款时间'
]
];
// 按搜索结果导出
where.sceneType = 'Content';
await listBszxPay(where)
.then((list) => {
bmList.value = list;
list?.forEach((d: BszxBm) => {
array.push([
`${d.userId}`,
`${d.name}`,
`${d.sex}`,
`${d.phone}`,
`${d.className}`,
`${d.gradeName}`,
`${d.address}`,
`${d.workUnit}`,
`${d.position}`,
`${d.present}`,
`${d.certificate}`,
`${d.createTime}`
]);
});
const sheetName = `导出捐款记录${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{ wch: 10 },
{ wch: 40 },
{ wch: 20 },
{ wch: 20 },
{ wch: 60 },
{ wch: 15 },
{ wch: 10 },
{ wch: 10 },
{ wch: 20 },
{ wch: 10 },
{ wch: 20 }
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {});
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,112 @@
<template>
<div class="cert-box h-screen">
<div class="cert-content" v-html="article.content"></div>
<div class="create-time text-right">
<span>广西百色中学</span>
<p>{{ toDateString(form.createTime, "YYYY-MM-dd") }}</p>
</div>
<!-- 二维码 -->
<div class="qrcode mt-10 text-center">
<ele-qr-code-svg :value="`${qrcode}`" :size="200" />
</div>
</div>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router';
import { onMounted, ref,unref, reactive } from 'vue';
import { assignObject, toDateString } from "ele-admin-pro";
import {getBszxPay} from '@/api/bszx/bszxPay';
import type { BszxPay } from '@/api/bszx/bszxPay/model';
import {getCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
// 二维码内容
const qrcode = ref('123');
// 当前编辑数据
// 是否显示编辑弹窗
// 是否显示批量移动弹窗
// 加载状态
const loading = ref(true);
// 捐款记录
const form = reactive<BszxPay>({
id: undefined,
age: undefined,
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
number: undefined,
price: undefined,
extra: undefined,
dateTime: undefined,
certificate: undefined,
formData: undefined,
formId: undefined,
userId: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
sortNumber: 100
});
// 文章
const article = reactive<CmsArticle>({
articleId: undefined,
content: undefined,
createTime: undefined
});
/* 查询 */
const query = () => {
loading.value = true;
const { currentRoute } = useRouter();
const { params } = unref(currentRoute);
console.log(params.id,'query')
getBszxPay(Number(params?.id)).then(data => {
assignObject(form, data);
if(data.formId && data.formId > 0){
getCmsArticle(data.formId).then(result => {
if(result.content){
result.content = result?.content.replace('校友', data?.name + '___校友');
result.content = result?.content.replace('人民币', '人民币 ' + data?.price);
qrcode.value = 'https://website.websoft.top/bszx/pay-cert/' + data?.id;
}
assignObject(article, result);
})
}
})
};
onMounted(() => {
query();
})
</script>
<script lang="ts">
export default {
name: 'BszxPay'
};
</script>
<style lang="less" scoped>
.cert-box{
width: 500px;
margin: 10px auto;
padding: 58px;
border-bottom: blue 1px solid;
background: url("https://oss.wsdns.cn/20250127/cb1088c3b1354a118477a0b1a3cdac41.png") no-repeat;
background-size: 100%;
}
.cert-content{
margin-top: 250px;
}
</style>

View File

@@ -0,0 +1,220 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑百色中学-捐款排行' : '添加百色中学-捐款排行'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="来源表ID项目名称" name="formId">
<a-input
allow-clear
placeholder="请输入来源表ID项目名称"
v-model:value="form.formId"
/>
</a-form-item>
<a-form-item label="数量" name="number">
<a-input
allow-clear
placeholder="请输入数量"
v-model:value="form.number"
/>
</a-form-item>
<a-form-item label="获得捐款总金额" name="totalPrice">
<a-input
allow-clear
placeholder="请输入获得捐款总金额"
v-model:value="form.totalPrice"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPayRanking, updateBszxPayRanking } from '@/api/bszx/bszxPayRanking';
import { BszxPayRanking } from '@/api/bszx/bszxPayRanking/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPayRanking | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPayRanking>({
id: undefined,
formId: undefined,
number: undefined,
totalPrice: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
bszxPayRankingId: undefined,
bszxPayRankingName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
bszxPayRankingName: [
{
required: true,
type: 'string',
message: '请填写百色中学-捐款排行名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPayRanking : addBszxPayRanking;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,69 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-range-picker
v-model:value="dateRange"
@change="search"
value-format="YYYY-MM-DD"
/>
<a-tooltip title="获得捐款总金额" class="flex px-4">
<span class="text-gray-400">捐款总金额</span>
<span class="text-gray-700 font-bold">{{ formatNumber(totalPriceAmount) }}</span>
</a-tooltip>
</a-space>
</template>
<script lang="ts" setup>
import useSearch from "@/utils/use-search";
import { watch,ref } from 'vue';
import { formatNumber } from 'ele-admin-pro/es';
import {BszxPayRankingParam} from "@/api/bszx/bszxPayRanking/model";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
totalPriceAmount?: number;
}>(),
{}
);
// 日期范围选择
const dateRange = ref<[string, string]>(['', '']);
const emit = defineEmits<{
(e: 'search', where?: BszxPayRankingParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where,resetFields} = useSearch<BszxPayRankingParam>({
id: undefined,
userId: undefined,
createTimeStart: undefined,
createTimeEnd: undefined,
keywords: ''
});
/* 搜索 */
const search = () => {
const [d1, d2] = dateRange.value ?? [];
emit('search', {
...where,
createTimeStart: d1 ? d1 + ' 00:00:00' : '',
createTimeEnd: d2 ? d2 + ' 23:59:59' : ''
});
};
const onSearch = (text: string) => {
where.sceneType = text
search();
}
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,223 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
:totalPriceAmount="totalPriceAmount.toFixed(2)"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxPayRankingEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxPayRankingEdit from './components/bszxPayRankingEdit.vue';
import {removeBszxPayRanking, removeBatchBszxPayRanking, ranking} from '@/api/bszx/bszxPayRanking';
import type {BszxPayRanking, BszxPayRankingParam} from '@/api/bszx/bszxPayRanking/model';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bszx/extra.vue";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxPayRanking[]>([]);
// 当前编辑数据
const current = ref<BszxPayRanking | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 合计总金额
const totalPriceAmount = ref<number>(0);
// 表格数据源
const datasource: DatasourceFunction = ({where}) => {
return ranking({...where}).then(data => {
totalPriceAmount.value = 0;
data.map((item) => {
if(item.totalPrice){
totalPriceAmount.value += item.totalPrice
}
})
return data;
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '项目名称',
dataIndex: 'formName',
key: 'formName'
},
{
title: '捐款人数',
dataIndex: 'number',
key: 'number',
align: 'center',
},
{
title: '获得捐款总金额',
dataIndex: 'totalPrice',
key: 'totalPrice',
align: 'center',
}
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: BszxPayRankingParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxPayRanking) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxPayRanking) => {
const hide = message.loading('请求中..', 0);
removeBszxPayRanking(row.bszxPayRankingId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxPayRanking(selection.value.map((d) => d.bszxPayRankingId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxPayRanking) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxPayRanking'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,220 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑百色中学-捐款排行' : '添加百色中学-捐款排行'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="来源表ID项目名称" name="formId">
<a-input
allow-clear
placeholder="请输入来源表ID项目名称"
v-model:value="form.formId"
/>
</a-form-item>
<a-form-item label="数量" name="number">
<a-input
allow-clear
placeholder="请输入数量"
v-model:value="form.number"
/>
</a-form-item>
<a-form-item label="获得捐款总金额" name="totalPrice">
<a-input
allow-clear
placeholder="请输入获得捐款总金额"
v-model:value="form.totalPrice"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addBszxPayRanking, updateBszxPayRanking } from '@/api/bszx/bszxPayRanking';
import { BszxPayRanking } from '@/api/bszx/bszxPayRanking/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: BszxPayRanking | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<BszxPayRanking>({
id: undefined,
formId: undefined,
number: undefined,
totalPrice: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
bszxPayRankingId: undefined,
bszxPayRankingName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
bszxPayRankingName: [
{
required: true,
type: 'string',
message: '请填写百色中学-捐款排行名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateBszxPayRanking : addBszxPayRanking;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,217 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-radio-group v-model:value="where.branch" @change="onBranch">
<a-radio-button :value="1">初中部</a-radio-button>
<a-radio-button :value="2">高中部</a-radio-button>
<!-- <a-radio-button :value="8">教职员工</a-radio-button>-->
<!-- <a-radio-button :value="9">其他</a-radio-button>-->
</a-radio-group>
<a-select
show-search
v-model:value="where.gradeId"
style="width: 240px"
placeholder="选择年级"
:options="gradeList"
@change="onGrade"
></a-select>
<a-select
v-if="where.gradeId"
show-search
v-model:value="where.name"
style="width: 240px"
placeholder="选择年级"
:options="classList"
@change="onClass"
></a-select>
<!-- <a-input-search-->
<!-- allow-clear-->
<!-- placeholder="请输入关键词"-->
<!-- style="width: 280px"-->
<!-- v-model:value="where.keywords"-->
<!-- @search="reload"-->
<!-- />-->
<!-- <a-button type="text" @click="handleExport">导出</a-button>-->
</a-space>
</template>
<script lang="ts" setup>
import {ref, watch} from 'vue';
import {utils, writeFile} from 'xlsx';
import dayjs from 'dayjs';
import {message} from 'ant-design-vue';
import {BszxBm} from "@/api/bszx/bszxBm/model";
import useSearch from "@/utils/use-search";
import {listBszxBm} from "@/api/bszx/bszxBm";
import {BszxClassParam} from "@/api/bszx/bszxClass/model";
import {listBszxGrade} from "@/api/bszx/bszxGrade";
import {listBszxClass} from "@/api/bszx/bszxClass";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: BszxClassParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const {where} = useSearch<BszxClassParam>({
id: undefined,
keywords: '',
name: undefined,
branch: undefined,
userId: undefined
});
// 变量
const loading = ref(false);
const bmList = ref<BszxBm[]>([])
const xlsFileName = ref<string>();
const gradeId = ref<number>();
const gradeList = ref<BszxClassParam[]>([]);
const classList = ref<BszxClassParam[]>([]);
const reload = () => {
emit('search', where);
};
const getGradeList = () => {
listBszxGrade({branch: where.branch}).then(res => {
gradeList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
};
const getClassList = () => {
listBszxClass({gradeId: gradeId.value}).then(res => {
classList.value = res.map(d => {
d.value = Number(d.id);
d.label = d.name;
return d;
});
})
}
const onBranch = () => {
getGradeList();
reload();
}
const onGrade = (gradeName: number, item: any) => {
where.gradeId = item.id;
if(gradeName){
console.log(item)
gradeId.value = item.id;
getClassList();
}
emit('search', where);
}
const onClass = (classId,item) => {
console.log(classId);
where.name = item.name;
reload();
}
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'用户ID',
'姓名',
'性别',
'手机号码',
'班级',
'年级',
'居住地址',
'工作单位',
'职务',
'是否能到场',
'邀请函',
'报名时间'
]
];
// 按搜索结果导出
where.sceneType = 'Content';
await listBszxBm(where)
.then((list) => {
bmList.value = list;
list?.forEach((d: BszxBm) => {
array.push([
`${d.userId}`,
`${d.name}`,
`${d.sex}`,
`${d.phone}`,
`${d.className}`,
`${d.gradeName}`,
`${d.address}`,
`${d.workUnit}`,
`${d.position}`,
`${d.present}`,
`${d.certificate}`,
`${d.createTime}`
]);
});
const sheetName = `导出报名列表${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10},
{wch: 40},
{wch: 20},
{wch: 20},
{wch: 60},
{wch: 15},
{wch: 10},
{wch: 10},
{wch: 20},
{wch: 10},
{wch: 20}
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {
});
};
getGradeList();
watch(
() => props.selection,
() => {
}
);
</script>

View File

@@ -0,0 +1,226 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<template #extra>
<Extra/>
</template>
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="id"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
:totalPriceAmount="totalPriceAmount.toFixed(2)"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50"/>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<BszxPayRankingEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import BszxPayRankingEdit from './components/bszxPayRankingEdit.vue';
import {removeBszxPayRanking, removeBatchBszxPayRanking, ranking2} from '@/api/bszx/bszxPayRanking';
import type {BszxPayRanking} from '@/api/bszx/bszxPayRanking/model';
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/bszx/extra.vue";
import {BszxPayParam} from "@/api/bszx/bszxPay/model";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<BszxPayRanking[]>([]);
// 当前编辑数据
const current = ref<BszxPayRanking | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 合计总金额
const totalPriceAmount = ref<number>(0);
// 表格数据源
const datasource: DatasourceFunction = ({where}) => {
return ranking2({...where}).then(data => {
return data;
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: 'ID',
dataIndex: 'id'
},
{
title: '分部',
dataIndex: 'branchName',
key: 'branchName',
},
{
title: '所在年级',
dataIndex: 'gradeName',
key: 'gradeName'
},
{
title: '班级',
dataIndex: 'name',
key: 'name'
},
{
title: '累计捐款金额',
dataIndex: 'totalMoney',
key: 'totalMoney',
align: 'center',
},
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: BszxPayParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = (row?: BszxPayRanking) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: BszxPayRanking) => {
const hide = message.loading('请求中..', 0);
removeBszxPayRanking(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchBszxPayRanking(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: BszxPayRanking) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
// openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'BszxPayRanking'
};
</script>
<style lang="less" scoped></style>

65
src/views/bszx/extra.vue Normal file
View File

@@ -0,0 +1,65 @@
<!-- 搜索表单 -->
<template>
<a-space style="flex-wrap: wrap" v-if="hasRole('superAdmin') || hasRole('admin') || hasRole('foundation')">
<a-button
type="text"
@click="openUrl('/bszx/ranking')"
>捐款排行榜
</a-button>
<a-button
type="text"
@click="openUrl('/bszx/ranking2')"
>千班万元
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import {watch,nextTick} from 'vue';
import {CmsWebsite} from '@/api/cms/cmsWebsite/model';
import {openUrl} from "@/utils/common";
import {message} from 'ant-design-vue';
import {removeSiteInfoCache} from "@/api/cms/cmsWebsite";
import {hasRole} from "@/utils/permission";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
website?: CmsWebsite;
count?: 0;
}>(),
{}
);
const emit = defineEmits<{
(e: 'add'): void;
}>();
const add = () => {
emit('add');
};
// 清除缓存
const clearSiteInfoCache = () => {
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId') + "*").then(
(msg) => {
if (msg) {
message.success(msg);
}
}
);
};
nextTick(() => {
if(localStorage.getItem('NotActive')){
// IsActive.value = false
}
})
watch(
() => props.selection,
() => {
}
);
</script>

View File

@@ -0,0 +1,41 @@
<template>
<div class="ele-body">
<a-card :bordered="false">
<div style="max-width: 960px; margin: 0 auto">
<a-result :status="success ? 'success' : ''" :title="title">
<template #extra>
<a-space size="middle">
<a-button danger @click="reload">清除缓存</a-button>
</a-space>
</template>
</a-result>
</div>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { removeSiteInfoCache } from '@/api/cms/cmsWebsite';
import { ref } from 'vue';
const success = ref<boolean>(false);
const title = ref<string>();
const reload = () => {
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then(
(msg) => {
if (msg) {
success.value = true;
title.value = '操作成功';
}
}
);
};
reload();
</script>
<script lang="ts">
export default {
name: 'ClearCache'
};
</script>

View File

@@ -0,0 +1,362 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="1000"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑广告' : '添加广告'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="标题" name="name">
<a-input
allow-clear
:maxlength="100"
style="width: 500px"
placeholder="请输入广告标题"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="类型" name="type">
<a-radio-group
v-model:value="form.type"
:disabled="isUpdate || !disabled"
@change="onTypeChange"
>
<a-radio :value="1">轮播</a-radio>
<a-radio :value="2">图片</a-radio>
<a-radio :value="3">视频</a-radio>
<a-radio :value="4">文本</a-radio>
</a-radio-group>
</a-form-item>
<template v-if="form.type == 1">
<a-form-item label="多图" name="images">
<div :class="`item-style ${form.style}`">
<SelectFile
:placeholder="`请选择图片`"
:limit="9"
:data="images"
@done="chooseFile"
@del="onDeleteItem"
/>
</div>
<a-space class="flex flex-col gap-2">
<div class="w-[500px]" v-for="(item, index) in images" :key="index">
<a-input
v-model:value="images[index].path"
placeholder="https://"
/>
</div>
</a-space>
</a-form-item>
</template>
<template v-if="form.type == 2">
<a-form-item label="单图" name="images">
<div :class="`item-style ${form.style}`">
<SelectFile
:limit="1"
:data="images"
@done="chooseFile"
@del="onDeleteItem"
/>
</div>
</a-form-item>
</template>
<template v-if="form.type == 3">
<a-form-item
label="视频"
name="images"
extra="请上传视频文件仅支持mp4格式大小200M以内"
>
<div :class="`item-style ${form.style}`">
<SelectFile
:placeholder="`请选择视频文件`"
:limit="1"
:data="images"
@done="chooseFile"
@del="onDeleteItem"
/>
</div>
</a-form-item>
</template>
<template v-if="form.type == 4">
<a-form-item
label="文本"
name="comments"
>
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入广告内容"
v-model:value="form.comments"
/>
</a-form-item>
</template>
<a-form-item label="链接" name="path" v-if="form.type == 4">
<a-input
v-model:value="form.path"
placeholder="https://"
/>
</a-form-item>
<a-form-item label="尺寸">
<a-space>
<a-input-number
allow-clear
:maxlength="3000"
placeholder="宽"
v-model:value="form.width"
/>
<span>X</span>
<a-input-number
allow-clear
:maxlength="2000"
placeholder="高"
v-model:value="form.height"
/>
</a-space>
<div class="pt-2">
<span class="text-gray-400">广告位尺寸大小(默认值100% * 500px)</span>
</div>
</a-form-item>
<a-form-item label="栏目">
<a-tree-select
allow-clear
:tree-data="navigationList"
tree-default-expand-all
style="width: 500px;"
placeholder="请选择栏目"
:value="form.categoryId || undefined"
:listHeight="700"
:dropdown-style="{ overflow: 'auto' }"
@update:value="(value?: number) => (form.categoryId = value)"
@change="onCategoryId"
/>
</a-form-item>
<a-form-item label="样式" name="style">
<a-input
allow-clear
placeholder="w-full border-solid border-gray-800 p-2 shadow-lg rounded-lg my-10"
v-model:value="form.style"
/>
<div class="pt-2 none">
<a class="text-sm text-gray-400" href="https://tailwindcss.com/docs/padding" target="_blank">Tailwind
Css使用教程</a>
</div>
</a-form-item>
<a-form-item label="描述" name="comments" v-if="form.type != 4">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入广告描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">开启</a-radio>
<a-radio :value="1">关闭</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref, reactive, watch} from 'vue';
import {Form, message} from 'ant-design-vue';
import {assignObject} from 'ele-admin-pro';
import {addCmsAd, updateCmsAd} from '@/api/cms/cmsAd';
import {CmsAd} from '@/api/cms/cmsAd/model';
import {useThemeStore} from '@/store/modules/theme';
import {storeToRefs} from 'pinia';
import {FormInstance, type Rule} from 'ant-design-vue/es/form';
import {useI18n} from 'vue-i18n';
import {FileRecord} from '@/api/system/file/model';
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
// 是否是修改
const isUpdate = ref(false);
const disabled = ref(true);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const {styleResponsive} = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsAd | null;
// 栏目导航
navigationList?: CmsNavigation[];
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 已上传数据
const images = ref<any[]>([]);
const {locale} = useI18n();
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
// 用户信息
const form = reactive<CmsAd>({
adId: undefined,
type: undefined,
categoryId: undefined,
name: '',
style: '',
images: '',
imageList: undefined,
width: '',
height: '',
path: '',
status: 0,
comments: '',
lang: locale.value || undefined,
sortNumber: 100,
merchantId: undefined
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
name: [
{
required: true,
type: 'string',
message: '请输入标题',
trigger: 'blur'
}
],
type: [
{
required: true,
type: 'number',
message: '请选择类型',
trigger: 'blur'
}
],
images: [
{
required: true,
type: 'string',
message: '请上传图片或视频',
trigger: 'blur',
validator: (_rule: Rule, value: string) => {
return new Promise<void>((resolve, reject) => {
if (images.value.length == 0) {
return reject('请上传图片或视频文件');
}
return resolve();
});
}
}
]
});
const {resetFields} = useForm(form, rules);
const chooseFile = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.downloadUrl + '?x-oss-process=image/resize,m_fixed,w_2000/quality,Q_90',
status: 'done'
});
form.images = data.downloadUrl + '?x-oss-process=image/resize,m_fixed,w_2000/quality,Q_90';
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.images = '';
};
// 选择栏目
const onCategoryId = (id: number) => {
form.categoryId = id;
};
const onTypeChange = () => {
disabled.value = !disabled.value;
}
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form,
images: JSON.stringify(images.value)
};
const saveOrUpdate = isUpdate.value ? updateCmsAd : addCmsAd;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
assignObject(form, props.data);
images.value = props.data.imageList;
isUpdate.value = true;
} else {
images.value = [];
disabled.value = true;
isUpdate.value = false;
}
} else {
resetFields();
}
},
{immediate: true}
);
</script>

View File

@@ -0,0 +1,84 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" :disabled="!websiteId" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
<a-tree-select
allow-clear
:tree-data="navigationList"
tree-default-expand-all
style="width: 240px"
:listHeight="700"
placeholder="请选择栏目"
:value="where.categoryId || undefined"
:dropdown-style="{ overflow: 'auto' }"
@update:value="(value?: number) => (where.categoryId = value)"
@change="onCategoryId"
/>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 280px"
v-model:value="where.keywords"
@search="reload"
/>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import { watch } from 'vue';
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
import {CmsAdParam} from "@/api/cms/cmsAd/model";
import useSearch from "@/utils/use-search";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
navigationList?: CmsNavigation[];
}>(),
{}
);
// 网站ID
const websiteId = localStorage.getItem('WebsiteId')
const emit = defineEmits<{
(e: 'search', where?: CmsAdParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 表单数据
const { where } = useSearch<CmsAdParam>({
adId: undefined,
pageId: undefined,
keywords: ''
});
// 新增
const add = () => {
emit('add');
};
// 按分类查询
const onCategoryId = (id: number) => {
where.categoryId = id;
emit('search', where);
};
const reload = () => {
emit('search', where);
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,288 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsAdId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
:navigationList="navigationList"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'type'">
<a-tag v-if="record.type == 1" color="pink">轮播</a-tag>
<a-tag v-if="record.type == 2" color="blue">图片</a-tag>
<a-tag v-if="record.type == 3" color="cyan">视频</a-tag>
<a-tag v-if="record.type == 4">文本</a-tag>
</template>
<template v-if="column.key === 'pageId'">
<span class="text-gray-400">{{ record.pageName }}</span>
</template>
<template v-if="column.key === 'categoryId'">
<span class="text-gray-400">{{ record.categoryName }}</span>
</template>
<template v-if="column.key === 'comments'">
<span class="text-gray-400">{{ record.comments }}</span>
</template>
<template v-if="column.key === 'images'">
<div :class="`item ${record.style}`">
<template
v-if="record.type != 4"
v-for="item in record.imageList"
:key="index"
>
<a-image :src="item.url" :width="80"/>
</template>
<template v-if="record.type == 4">
{{ record.comments }}
</template>
</div>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsAdEdit v-model:visible="showEdit" :data="current" :navigationList="navigationList" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {ExclamationCircleOutlined} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import {toTreeData} from 'ele-admin-pro';
import {useI18n} from 'vue-i18n';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsAdEdit from './components/cmsAdEdit.vue';
import {pageCmsAd, removeCmsAd, removeBatchCmsAd} from '@/api/cms/cmsAd';
import type {CmsAd, CmsAdParam} from '@/api/cms/cmsAd/model';
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
import {listCmsNavigation} from "@/api/cms/cmsNavigation";
import {getPageTitle} from "@/utils/common";
import Extra from "@/views/cms/cmsNavigation/components/extra.vue";
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 国际化
const {locale} = useI18n();
// 表格选中数据
const selection = ref<CmsAd[]>([]);
// 当前编辑数据
const current = ref<CmsAd | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 栏目数据
const navigationList = ref<CmsNavigation[]>();
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
where.lang = locale.value || undefined;
return pageCmsAd({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
width: 90,
dataIndex: 'adId'
},
{
title: '名称',
dataIndex: 'name',
key: 'name',
ellipsis: true,
width: 180
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
align: 'center',
width: 120
},
{
title: '广告图片',
dataIndex: 'images',
key: 'images'
},
{
title: '栏目名称',
dataIndex: 'categoryId',
key: 'categoryId',
align: 'center',
width: 180
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center',
width: 120
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsAdParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsAd) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsAd) => {
const hide = message.loading('请求中..', 0);
removeCmsAd(row.adId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsAd(selection.value.map((d) => d.adId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
// 加载栏目数据
if (!navigationList.value) {
listCmsNavigation({}).then((res) => {
navigationList.value = toTreeData({
data: res?.map((d) => {
d.value = d.navigationId;
d.label = d.title;
if (!d.component) {
d.disabled = true;
}
return d;
}),
idField: 'navigationId',
parentIdField: 'parentId'
});
});
}
};
/* 自定义行属性 */
const customRow = (record: CmsAd) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsAd'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,220 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑广告图片' : '添加广告图片'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="广告标题" name="title">
<a-input
allow-clear
placeholder="请输入广告标题"
v-model:value="form.title"
/>
</a-form-item>
<a-form-item label="图片地址" name="path">
<a-input
allow-clear
placeholder="请输入图片地址"
v-model:value="form.path"
/>
</a-form-item>
<a-form-item label="链接地址" name="url">
<a-input
allow-clear
placeholder="请输入链接地址"
v-model:value="form.url"
/>
</a-form-item>
<a-form-item label="广告位ID" name="adId">
<a-input
allow-clear
placeholder="请输入广告位ID"
v-model:value="form.adId"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsAdRecord, updateCmsAdRecord } from '@/api/cms/cmsAdRecord';
import { CmsAdRecord } from '@/api/cms/cmsAdRecord/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsAdRecord | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsAdRecord>({
adRecordId: undefined,
title: undefined,
path: undefined,
url: undefined,
adId: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
tenantId: undefined,
createTime: undefined,
cmsAdRecordId: undefined,
cmsAdRecordName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsAdRecordName: [
{
required: true,
type: 'string',
message: '请填写广告图片名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsAdRecord : addCmsAdRecord;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,257 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsAdRecordId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsAdRecordEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsAdRecordEdit from './components/cmsAdRecordEdit.vue';
import { pageCmsAdRecord, removeCmsAdRecord, removeBatchCmsAdRecord } from '@/api/cms/cmsAdRecord';
import type { CmsAdRecord, CmsAdRecordParam } from '@/api/cms/cmsAdRecord/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsAdRecord[]>([]);
// 当前编辑数据
const current = ref<CmsAdRecord | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsAdRecord({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'adRecordId',
key: 'adRecordId',
align: 'center',
width: 90,
},
{
title: '广告标题',
dataIndex: 'title',
key: 'title',
align: 'center',
},
{
title: '图片地址',
dataIndex: 'path',
key: 'path',
align: 'center',
},
{
title: '链接地址',
dataIndex: 'url',
key: 'url',
align: 'center',
},
{
title: '广告位ID',
dataIndex: 'adId',
key: 'adId',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsAdRecordParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsAdRecord) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsAdRecord) => {
const hide = message.loading('请求中..', 0);
removeCmsAdRecord(row.cmsAdRecordId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsAdRecord(selection.value.map((d) => d.cmsAdRecordId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsAdRecord) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsAdRecord'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,82 @@
<!-- 用户导入弹窗 -->
<template>
<ele-modal
:width="520"
:footer="null"
title="文章批量导入"
:visible="visible"
@update:visible="updateVisible"
>
<a-spin :spinning="loading">
<a-upload-dragger
accept=".xls,.xlsx"
:show-upload-list="false"
:customRequest="doUpload"
style="padding: 24px 0; margin-bottom: 16px"
>
<p class="ant-upload-drag-icon">
<cloud-upload-outlined />
</p>
<p class="ant-upload-hint">将文件拖到此处或点击上传</p>
</a-upload-dragger>
</a-spin>
<div class="ele-text-center">
<span>只能上传xlsxlsx文件导入模板和导出模板格式一致</span>
</div>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { message } from 'ant-design-vue/es';
import { CloudUploadOutlined } from '@ant-design/icons-vue';
import {importArticles} from "@/api/cms/cmsArticle";
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
defineProps<{
// 是否打开弹窗
visible: boolean;
}>();
// 导入请求状态
const loading = ref(false);
/* 上传 */
const doUpload = ({ file }) => {
if (
![
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
].includes(file.type)
) {
message.error('只能选择 excel 文件');
return false;
}
if (file.size / 1024 / 1024 > 10) {
message.error('大小不能超过 10MB');
return false;
}
loading.value = true;
importArticles(file)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
return false;
};
/* 更新 visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,207 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="600"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="'批量更新'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 24, sm: 24, xs: 24 } : { flex: '1' }
"
>
<a-form-item>
<a-alert :message="'提示:每次最多修改 50000 条数据'" banner />
</a-form-item>
<a-form-item label="选择栏目">
<a-tree-select
allow-clear
:tree-data="navigationList"
tree-default-expand-all
style="width: 400px"
placeholder="请选择栏目"
:value="form.categoryId || undefined"
:dropdown-style="{ maxHeight: '360px', overflow: 'auto' }"
@update:value="(value?: number) => (form.categoryId = value)"
@change="onCategoryId"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { updateBatchCmsArticle } from "@/api/cms/cmsArticle";
import { CmsArticle } from '@/api/cms/cmsArticle/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance, RuleObject } from 'ant-design-vue/es/form';
import { CmsNavigation } from "@/api/cms/cmsNavigation/model";
// 是否是修改
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 表格选中数据
selection?: CmsArticle[];
// 栏目数据
navigationList?: CmsNavigation[];
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const content = ref('');
// 用户信息
const form = reactive<CmsArticle>({
articleId: undefined,
// 文章模型
model: 'detail',
// 封面图
image: '',
// 文章标题
title: '',
type: 0,
// 展现方式
showType: 10,
// 文章来源
source: '-',
// 文章类型
categoryId: undefined,
// 栏目名称
categoryName: undefined,
// 文章内容
content: '',
// 虚拟阅读量
virtualViews: 0,
// 实际阅读量
actualViews: 0,
permission: 0,
password: undefined,
password2: undefined,
// 用户ID
userId: undefined,
files: '',
// 排序
sortNumber: 100,
// 备注
comments: undefined,
// 状态
status: 1,
// 创建时间
createTime: '',
// 更新时间
updateTime: ''
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
title: [
{
required: true,
message: '请选择文章标题',
type: 'string',
trigger: 'blur'
}
],
categoryId: [
{
required: true,
message: '请选择栏目',
type: 'number',
trigger: 'blur'
}
],
content: [
{
required: true,
type: "string",
message: "请输入文章内容",
trigger: "blur",
validator: async (_rule: RuleObject, value: string) => {
if (content.value == "") {
return Promise.reject("请输入文字内容");
}
return Promise.resolve();
}
}
],
});
// 选择栏目
const onCategoryId = (id: number) => {
console.log(id);
form.categoryId = id;
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
props.selection?.map(d => d.articleId)
updateBatchCmsArticle({
ids: props.selection?.map(d => d.articleId),
data: {
categoryId: form.categoryId
}
}).then((msg) => {
message.success(msg);
loading.value = false;
updateVisible(false);
emit('done');
})
.catch((e) => {
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,327 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" :disabled="!websiteId" @click="add">
<template #icon>
<PlusOutlined/>
</template>
<span>添加</span>
</a-button>
<a-button
danger
type="primary"
class="ele-btn-icon"
:disabled="selection?.length === 0"
@click="removeBatch"
>
<template #icon>
<DeleteOutlined/>
</template>
<span>批量删除</span>
</a-button>
<a-button
type="primary"
:disabled="selection?.length === 0"
@click="updateBatch"
>
移动
</a-button>
<a-radio-group v-model:value="type" @change="handleSearch" v-if="setting.setting?.articleReview">
<a-radio-button type="text" value="已发布"
>已发布({{ articleCount?.totalNum }})
</a-radio-button
>
<a-radio-button value="待审核"
>待审核({{ articleCount?.totalNum2 }})
</a-radio-button
>
<a-radio-button value="已驳回"
>已驳回({{ articleCount?.totalNum3 }})
</a-radio-button
>
</a-radio-group>
<a-tree-select
allow-clear
:tree-data="navigationList"
tree-default-expand-all
style="width: 240px"
:listHeight="700"
placeholder="请选择栏目"
:value="where.categoryId || undefined"
:dropdown-style="{ overflow: 'auto' }"
@update:value="(value?: number) => (where.categoryId = value)"
@change="onCategoryId"
/>
<ChooseDictionary
v-if="showChooseDict"
dict-code="NavigationModel"
:placeholder="`选择模型`"
style="width: 120px"
v-model:value="where.model"
@done="chooseModel"
/>
<a-input-search
allow-clear
placeholder="请输入关键词"
style="width: 240px"
v-model:value="where.keywords"
@search="reload"
/>
<a-button type="text" @click="reset">重置</a-button>
<a-button type="text" :disabled="!websiteId" @click="handleExport">导出xls</a-button>
<a-button type="text" :disabled="!websiteId" @click="openImport">导入xls</a-button>
</a-space>
<!-- 导入弹窗 -->
<import v-model:visible="showImport" @done="reload"/>
</template>
<script lang="ts" setup>
import {DeleteOutlined, PlusOutlined} from '@ant-design/icons-vue';
import {ref, watch} from 'vue';
import {message} from 'ant-design-vue';
import {utils, writeFile} from 'xlsx';
import {getCount, listCmsArticle} from '@/api/cms/cmsArticle';
import type {CmsArticle, CmsArticleCount, CmsArticleParam} from '@/api/cms/cmsArticle/model';
import useSearch from '@/utils/use-search';
import {CmsNavigation} from '@/api/cms/cmsNavigation/model';
import dayjs from 'dayjs';
import Import from "./Import.vue";
import {useWebsiteSettingStore} from "@/store/modules/setting";
import {openUrl, push} from "@/utils/common";
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: CmsArticle[];
merchantId?: number;
navigationList?: CmsNavigation[];
categoryId?: number;
}>(),
{}
);
const type = ref<string>();
const setting = useWebsiteSettingStore();
// 统计数据
const articleCount = ref<CmsArticleCount>();
const showChooseDict = ref<boolean>(false);
// 请求状态
const loading = ref(false);
const xlsFileName = ref<string>();
const articleList = ref<CmsArticle[]>([]);
// 是否显示用户导入弹窗
const showImport = ref(false);
const websiteId = localStorage.getItem('WebsiteId')
// 表单数据
const {where, resetFields} = useSearch<CmsArticleParam>({
articleId: undefined,
model: undefined,
categoryId: undefined,
merchantId: undefined,
keywords: '',
sceneType: undefined,
userId: undefined
});
const emit = defineEmits<{
(e: 'search', where?: CmsArticleParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
// 批量删除
const removeBatch = () => {
emit('remove');
};
// 批量更新
const updateBatch = () => {
emit('batchMove');
}
/* 打开编辑弹窗 */
const openImport = () => {
showImport.value = true;
};
const handleSearch = (e) => {
const text = e.target.value;
if (text === '已发布') {
where.status = 0;
}
if (text === '待审核') {
where.status = 1;
}
if (text === '已驳回') {
where.status = 2;
}
emit('search', where);
};
const reload = () => {
getCount(where).then((data: any) => {
articleCount.value = data;
});
emit('search', where);
};
// 按模型查找
const chooseModel = () => {
emit('search', where);
};
// 按分类查询
const onCategoryId = (id: number) => {
where.categoryId = id;
emit('search', where);
};
// 导出
const handleExport = async () => {
loading.value = true;
const array: (string | number)[][] = [
[
'文章ID',
'文章标题',
'封面图片',
'所属栏目',
'栏目ID',
'父级栏目名称',
'父级栏目ID',
'文章内容',
'摘要',
'来源',
'实际阅读量',
'作者',
'发布时间',
'类型',
'模型',
'详情页模板',
'话题',
'关键词',
'产品概述',
'显示方式',
'客户端',
'文件列表',
'视频地址',
'点赞数',
'评论数',
'推荐',
'用户ID',
'商户ID',
'语言',
'排序',
`状态`,
'租户ID'
]
];
// 按搜索结果导出
where.sceneType = 'Content';
await listCmsArticle(where)
.then((list) => {
articleList.value = list;
list?.forEach((d: CmsArticle) => {
array.push([
`${d.articleId}`,
`${d.title}`,
`${d.image}`,
`${d.categoryName}`,
`${d.categoryId}`,
`${d.parentName}`,
`${d.parentId}`,
`${d.content}`,
`${d.comments}`,
`${d.source}`,
`${d.actualViews}`,
`${d.author}`,
`${d.createTime}`,
`${d.type}`,
`${d.model}`,
`${d.detail}`,
`${d.topic}`,
`${d.tags}`,
`${d.overview}`,
`${d.showType}`,
`${d.platform}`,
`${d.files}`,
`${d.video}`,
`${d.likes}`,
`${d.commentNumbers}`,
`${d.recommend}`,
`${d.userId}`,
`${d.merchantId}`,
`${d.lang}`,
`${d.sortNumber}`,
`${d.status}`,
`${d.tenantId}`
]);
});
const sheetName = `导出文章列表${dayjs(new Date()).format('YYYYMMDD')}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10},
{wch: 40},
{wch: 20},
{wch: 20},
{wch: 60},
{wch: 15},
{wch: 10},
{wch: 10},
{wch: 20},
{wch: 10},
{wch: 20}
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${
where.createTimeEnd ? xlsFileName.value + '_' : ''
}${sheetName}.xlsx`
);
loading.value = false;
}, 1000);
})
.catch((msg) => {
message.error(msg);
loading.value = false;
})
.finally(() => {
});
};
/* 重置 */
const reset = () => {
resetFields();
type.value = undefined;
reload();
openUrl(`/website/article`)
};
watch(
() => props.merchantId,
() => {
if (props.categoryId) {
where.categoryId = props.categoryId;
}
reload();
},
{immediate: true}
);
</script>

View File

@@ -0,0 +1,51 @@
<!-- 文章来源选择下拉框 -->
<template>
<a-select
optionFilterProp="label"
:options="data"
allow-clear
:value="value"
:placeholder="placeholder"
@update:value="updateValue"
@blur="onBlur"
@change="onChange"
/>
</template>
<script lang="ts" setup>
import { getDictionaryOptions } from '@/utils/common';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'blur'): void;
(e: 'change'): void;
}>();
withDefaults(
defineProps<{
value?: string;
placeholder?: string;
}>(),
{
placeholder: '请选择文章来源'
}
);
// 字典数据
const data = getDictionaryOptions('articleSource');
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
/* 选择事件 */
const onChange = () => {
emit('change');
};
</script>

View File

@@ -0,0 +1,468 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="articleId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
v-model:selection="selection"
:scroll="{ x: 1200 }"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
:navigationList="navigationList"
:merchantId="merchantId"
:categoryId="categoryId"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'title'">
<span class="text-black">{{ record.title }}</span>
</template>
<template v-if="column.key === 'categoryName'">
<a-tooltip>
<template #title>
{{ record.categoryId }}
</template>
<span class="text-gray-400">{{ record.categoryName }}</span>
</a-tooltip>
</template>
<template v-if="column.key === 'type'">
<a-tag v-if="record.type === 0">虚拟文章</a-tag>
<a-tag v-if="record.type === 1">实物文章</a-tag>
</template>
<template v-if="column.key === 'actualViews'">
<span class="text-gray-400">{{ record.actualViews }}</span>
</template>
<template v-if="column.key === 'image'">
<a-image
:src="record.image"
:width="50"
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
/>
</template>
<template v-if="column.key === 'salePrice'">
{{ formatNumber(record.salePrice) }}
</template>
<template v-if="column.key === 'status'">
<a-tag
:color="record.status == 0 ? 'green' : 'red'"
class="cursor-pointer"
@click="onUpdate(record)"
>{{ record.statusText }}
</a-tag>
</template>
<template v-if="column.key === 'recommend'">
<a-space @click="onRecommend(record)">
<span v-if="record.recommend === 1" class="ele-text-success"
><CheckOutlined
/></span>
<span v-else class="ele-text-placeholder"><CloseOutlined/></span>
</a-space>
</template>
<template v-if="column.key === 'action'">
<a @click="onShare(record)">预览</a>
<a-divider type="vertical"/>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical"/>
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<ArticleEdit
v-model:visible="showEdit"
:navigationList="navigationList"
:categoryList="categoryList"
:categoryId="categoryId"
:data="current"
@done="reload"
/>
<!-- 批量更新 -->
<ArticleUpdate
v-model:visible="showMove"
:navigationList="navigationList"
:selection="selection"
@done="reload"
/>
<!-- 二维码 -->
<Qrcode
v-model:visible="showQrcode"
:data="current?.url"
@done="hideShare"
/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref, watch} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {useI18n} from 'vue-i18n';
import {
CheckOutlined,
CloseOutlined,
ExclamationCircleOutlined
} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import ArticleEdit from './components/articleEdit.vue';
import ArticleUpdate from './components/articleUpdate.vue';
import {
pageCmsArticle,
removeCmsArticle,
removeBatchCmsArticle,
updateCmsArticle, getCmsArticle
} from '@/api/cms/cmsArticle';
import type {CmsArticle, CmsArticleParam} from '@/api/cms/cmsArticle/model';
import {formatNumber} from 'ele-admin-pro/es';
import router from '@/router';
import {toTreeData} from 'ele-admin-pro';
import {toDateString} from 'ele-admin-pro';
import {
detail,
getPageTitle
} from '@/utils/common';
import {listCmsNavigation} from '@/api/cms/cmsNavigation';
import {CmsNavigation} from '@/api/cms/cmsNavigation/model';
import {CmsArticleCategory} from '@/api/cms/cmsArticleCategory/model';
import {listCmsArticleCategory} from '@/api/cms/cmsArticleCategory';
import Qrcode from '@/components/QrCode/index.vue';
import {getCmsWebsiteSetting} from '@/api/cms/cmsWebsiteSetting';
import {useWebsiteSettingStore} from '@/store/modules/setting';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 国际化
const {locale} = useI18n();
// 表格选中数据
const selection = ref<CmsArticle[]>([]);
// 当前编辑数据
const current = ref<CmsArticle | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 店铺ID
const merchantId = ref<number>();
// 栏目ID
const categoryId = ref<number>();
// 栏目数据
const navigationList = ref<CmsNavigation[]>();
// 文章分类
const categoryList = ref<CmsArticleCategory[]>();
// 是否显示二维码
const showQrcode = ref(false);
// 网站设置信息
const setting = useWebsiteSettingStore();
// 表格数据源
const datasource: DatasourceFunction = ({page, limit, where, orders}) => {
if (categoryId.value) {
where.categoryId = categoryId.value;
}
where.lang = locale.value || undefined;
return pageCmsArticle({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'articleId',
key: 'articleId',
align: 'center',
hideInTable: true,
width: 90
},
{
title: '封面图',
dataIndex: 'image',
key: 'image',
width: 120,
align: 'center'
},
{
title: '文章标题',
dataIndex: 'title',
key: 'title'
},
{
title: '栏目名称',
dataIndex: 'categoryName',
key: 'categoryName',
width: 120,
align: 'center'
},
{
title: '所属栏目',
dataIndex: 'categoryId',
key: 'categoryId',
align: 'center',
hideInTable: true
},
{
title: '实际阅读量',
dataIndex: 'actualViews',
key: 'actualViews',
sorter: true,
width: 120,
align: 'center'
},
{
title: '虚拟阅读量',
dataIndex: 'virtualViews',
key: 'virtualViews',
width: 120,
align: 'center',
hideInTable: true
},
{
title: '推荐',
dataIndex: 'recommend',
key: 'recommend',
sorter: true,
width: 120,
align: 'center'
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
sorter: true,
width: 120,
align: 'center'
},
{
title: '排序号',
dataIndex: 'sortNumber',
key: 'sortNumber',
sorter: true,
width: 120,
align: 'center'
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
customRender: ({text}) => toDateString(text, 'yyyy-MM-dd'),
sorter: true
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsArticleParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 打开编辑弹窗 */
const openEdit = async (row?: CmsArticle) => {
current.value = row ?? null;
if(row?.articleId){
current.value = await getCmsArticle(row?.articleId);
}
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
// 分享二维码
const onShare = (row?: CmsArticle) => {
const data = {
isMobile: true,
...row
};
data.qrcode = `${detail(data)}`;
data.url = `${detail(row)}`;
current.value = data ?? null;
showQrcode.value = true;
};
const hideShare = () => {
showQrcode.value = false;
};
const onUpdate = (row?: CmsArticle) => {
const status = row?.status == 0 ? 1 : 0;
updateCmsArticle({...row, status}).then((msg) => {
message.success(msg);
reload();
});
};
const onRecommend = (row: CmsArticle) => {
updateCmsArticle({
...row,
recommend: row.recommend == 1 ? 0 : 1
}).then((msg) => {
message.success(msg);
reload();
});
};
/* 删除单个 */
const remove = (row: CmsArticle) => {
const hide = message.loading('请求中..', 0);
removeCmsArticle(row.articleId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsArticle(selection.value.map((d) => d.articleId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 自定义行属性 */
const customRow = (record: CmsArticle) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
// 加载栏目数据
if (!navigationList.value) {
listCmsNavigation({}).then((res) => {
navigationList.value = toTreeData({
data: res?.map((d) => {
d.value = d.navigationId;
d.label = d.title;
if (!d.component) {
d.disabled = true;
}
if (
d.model == 'index' ||
d.model == 'page' ||
d.model == 'order'
) {
d.disabled = true;
}
return d;
}),
idField: 'navigationId',
parentIdField: 'parentId'
});
});
}
// 加载文章分类
if (!categoryList.value) {
listCmsArticleCategory({}).then((res) => {
categoryList.value = toTreeData({
data: res?.map((d) => {
d.value = d.categoryId;
d.label = d.title;
return d;
}),
idField: 'categoryId',
parentIdField: 'parentId'
});
});
}
const getSetting = () => {
// 网站设置信息
getCmsWebsiteSetting(Number(localStorage.getItem('WebsiteId'))).then(
(res) => {
if (res.editor) {
setting.value = res;
localStorage.setItem('Editor', res.editor);
}
}
);
};
watch(
() => router.currentRoute.value.query,
(query) => {
if (query) {
categoryId.value = Number(query.id);
getSetting();
reload();
}
},
{immediate: true}
);
</script>
<script lang="ts">
export default {
name: 'CmsArticleV2'
};
</script>

View File

@@ -0,0 +1,312 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑文章分类表' : '添加文章分类表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="分类标识" name="categoryCode">
<a-input
allow-clear
placeholder="请输入分类标识"
v-model:value="form.categoryCode"
/>
</a-form-item>
<a-form-item label="分类名称" name="title">
<a-input
allow-clear
placeholder="请输入分类名称"
v-model:value="form.title"
/>
</a-form-item>
<a-form-item label="类型 0列表 1单页 2外链" name="type">
<a-input
allow-clear
placeholder="请输入类型 0列表 1单页 2外链"
v-model:value="form.type"
/>
</a-form-item>
<a-form-item
label="分类图片"
name="image">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseImage"
@del="onDeleteItem"
/>
</a-form-item>
<a-form-item label="上级分类ID" name="parentId">
<a-input
allow-clear
placeholder="请输入上级分类ID"
v-model:value="form.parentId"
/>
</a-form-item>
<a-form-item label="路由/链接地址" name="path">
<a-input
allow-clear
placeholder="请输入路由/链接地址"
v-model:value="form.path"
/>
</a-form-item>
<a-form-item label="组件路径" name="component">
<a-input
allow-clear
placeholder="请输入组件路径"
v-model:value="form.component"
/>
</a-form-item>
<a-form-item label="绑定的页面" name="pageId">
<a-input
allow-clear
placeholder="请输入绑定的页面"
v-model:value="form.pageId"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="文章数量" name="count">
<a-input
allow-clear
placeholder="请输入文章数量"
v-model:value="form.count"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)" name="hide">
<a-input
allow-clear
placeholder="请输入是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)"
v-model:value="form.hide"
/>
</a-form-item>
<a-form-item label="是否推荐" name="recommend">
<a-input
allow-clear
placeholder="请输入是否推荐"
v-model:value="form.recommend"
/>
</a-form-item>
<a-form-item label="是否显示在首页" name="showIndex">
<a-input
allow-clear
placeholder="请输入是否显示在首页"
v-model:value="form.showIndex"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1禁用" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsArticleCategory, updateCmsArticleCategory } from '@/api/cms/cmsArticleCategory';
import { CmsArticleCategory } from '@/api/cms/cmsArticleCategory/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsArticleCategory | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsArticleCategory>({
categoryId: undefined,
categoryCode: undefined,
title: undefined,
type: undefined,
image: undefined,
parentId: undefined,
path: undefined,
component: undefined,
pageId: undefined,
userId: undefined,
count: undefined,
sortNumber: undefined,
comments: undefined,
hide: undefined,
recommend: undefined,
showIndex: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
cmsArticleCategoryId: undefined,
cmsArticleCategoryName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsArticleCategoryName: [
{
required: true,
type: 'string',
message: '请填写文章分类表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsArticleCategory : addCmsArticleCategory;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,323 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsArticleCategoryId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsArticleCategoryEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsArticleCategoryEdit from './components/cmsArticleCategoryEdit.vue';
import { pageCmsArticleCategory, removeCmsArticleCategory, removeBatchCmsArticleCategory } from '@/api/cms/cmsArticleCategory';
import type { CmsArticleCategory, CmsArticleCategoryParam } from '@/api/cms/cmsArticleCategory/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsArticleCategory[]>([]);
// 当前编辑数据
const current = ref<CmsArticleCategory | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsArticleCategory({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '文章分类ID',
dataIndex: 'categoryId',
key: 'categoryId',
align: 'center',
width: 90,
},
{
title: '分类标识',
dataIndex: 'categoryCode',
key: 'categoryCode',
align: 'center',
},
{
title: '分类名称',
dataIndex: 'title',
key: 'title',
align: 'center',
},
{
title: '类型 0列表 1单页 2外链',
dataIndex: 'type',
key: 'type',
align: 'center',
},
{
title: '分类图片',
dataIndex: 'image',
key: 'image',
align: 'center',
},
{
title: '上级分类ID',
dataIndex: 'parentId',
key: 'parentId',
align: 'center',
},
{
title: '路由/链接地址',
dataIndex: 'path',
key: 'path',
align: 'center',
},
{
title: '组件路径',
dataIndex: 'component',
key: 'component',
align: 'center',
},
{
title: '绑定的页面',
dataIndex: 'pageId',
key: 'pageId',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '文章数量',
dataIndex: 'count',
key: 'count',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)',
dataIndex: 'hide',
key: 'hide',
align: 'center',
},
{
title: '是否推荐',
dataIndex: 'recommend',
key: 'recommend',
align: 'center',
},
{
title: '是否显示在首页',
dataIndex: 'showIndex',
key: 'showIndex',
align: 'center',
},
{
title: '状态, 0正常, 1禁用',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsArticleCategoryParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsArticleCategory) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsArticleCategory) => {
const hide = message.loading('请求中..', 0);
removeCmsArticleCategory(row.cmsArticleCategoryId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsArticleCategory(selection.value.map((d) => d.cmsArticleCategoryId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsArticleCategory) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsArticleCategory'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,268 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑文章评论表' : '添加文章评论表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="文章ID" name="articleId">
<a-input
allow-clear
placeholder="请输入文章ID"
v-model:value="form.articleId"
/>
</a-form-item>
<a-form-item label="评分 (10好评 20中评 30差评)" name="score">
<a-input
allow-clear
placeholder="请输入评分 (10好评 20中评 30差评)"
v-model:value="form.score"
/>
</a-form-item>
<a-form-item label="评价内容" name="content">
<a-input
allow-clear
placeholder="请输入评价内容"
v-model:value="form.content"
/>
</a-form-item>
<a-form-item label="是否为图片评价" name="isPicture">
<a-input
allow-clear
placeholder="请输入是否为图片评价"
v-model:value="form.isPicture"
/>
</a-form-item>
<a-form-item label="评论者ID" name="userId">
<a-input
allow-clear
placeholder="请输入评论者ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="被评价者ID" name="toUserId">
<a-input
allow-clear
placeholder="请输入被评价者ID"
v-model:value="form.toUserId"
/>
</a-form-item>
<a-form-item label="回复的评论ID" name="replyCommentId">
<a-input
allow-clear
placeholder="请输入回复的评论ID"
v-model:value="form.replyCommentId"
/>
</a-form-item>
<a-form-item label="回复者ID" name="replyUserId">
<a-input
allow-clear
placeholder="请输入回复者ID"
v-model:value="form.replyUserId"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0未读, 1已读" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsArticleComment, updateCmsArticleComment } from '@/api/cms/cmsArticleComment';
import { CmsArticleComment } from '@/api/cms/cmsArticleComment/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsArticleComment | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsArticleComment>({
commentId: undefined,
articleId: undefined,
score: undefined,
content: undefined,
isPicture: undefined,
userId: undefined,
toUserId: undefined,
replyCommentId: undefined,
replyUserId: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
cmsArticleCommentId: undefined,
cmsArticleCommentName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsArticleCommentName: [
{
required: true,
type: 'string',
message: '请填写文章评论表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsArticleComment : addCmsArticleComment;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,293 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsArticleCommentId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsArticleCommentEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsArticleCommentEdit from './components/cmsArticleCommentEdit.vue';
import { pageCmsArticleComment, removeCmsArticleComment, removeBatchCmsArticleComment } from '@/api/cms/cmsArticleComment';
import type { CmsArticleComment, CmsArticleCommentParam } from '@/api/cms/cmsArticleComment/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsArticleComment[]>([]);
// 当前编辑数据
const current = ref<CmsArticleComment | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsArticleComment({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '评价ID',
dataIndex: 'commentId',
key: 'commentId',
align: 'center',
width: 90,
},
{
title: '文章ID',
dataIndex: 'articleId',
key: 'articleId',
align: 'center',
},
{
title: '评分 (10好评 20中评 30差评)',
dataIndex: 'score',
key: 'score',
align: 'center',
},
{
title: '评价内容',
dataIndex: 'content',
key: 'content',
align: 'center',
},
{
title: '是否为图片评价',
dataIndex: 'isPicture',
key: 'isPicture',
align: 'center',
},
{
title: '评论者ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '被评价者ID',
dataIndex: 'toUserId',
key: 'toUserId',
align: 'center',
},
{
title: '回复的评论ID',
dataIndex: 'replyCommentId',
key: 'replyCommentId',
align: 'center',
},
{
title: '回复者ID',
dataIndex: 'replyUserId',
key: 'replyUserId',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0未读, 1已读',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsArticleCommentParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsArticleComment) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsArticleComment) => {
const hide = message.loading('请求中..', 0);
removeCmsArticleComment(row.cmsArticleCommentId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsArticleComment(selection.value.map((d) => d.cmsArticleCommentId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsArticleComment) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsArticleComment'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,178 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑文章记录表' : '添加文章记录表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="文章ID" name="articleId">
<a-input
allow-clear
placeholder="请输入文章ID"
v-model:value="form.articleId"
/>
</a-form-item>
<a-form-item label="文章内容" name="content">
<a-input
allow-clear
placeholder="请输入文章内容"
v-model:value="form.content"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsArticleContent, updateCmsArticleContent } from '@/api/cms/cmsArticleContent';
import { CmsArticleContent } from '@/api/cms/cmsArticleContent/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsArticleContent | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsArticleContent>({
id: undefined,
articleId: undefined,
content: undefined,
tenantId: undefined,
createTime: undefined,
cmsArticleContentId: undefined,
cmsArticleContentName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsArticleContentName: [
{
required: true,
type: 'string',
message: '请填写文章记录表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsArticleContent : addCmsArticleContent;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,227 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsArticleContentId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsArticleContentEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsArticleContentEdit from './components/cmsArticleContentEdit.vue';
import { pageCmsArticleContent, removeCmsArticleContent, removeBatchCmsArticleContent } from '@/api/cms/cmsArticleContent';
import type { CmsArticleContent, CmsArticleContentParam } from '@/api/cms/cmsArticleContent/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsArticleContent[]>([]);
// 当前编辑数据
const current = ref<CmsArticleContent | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsArticleContent({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '文章ID',
dataIndex: 'articleId',
key: 'articleId',
align: 'center',
},
{
title: '文章内容',
dataIndex: 'content',
key: 'content',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsArticleContentParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsArticleContent) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsArticleContent) => {
const hide = message.loading('请求中..', 0);
removeCmsArticleContent(row.cmsArticleContentId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsArticleContent(selection.value.map((d) => d.cmsArticleContentId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsArticleContent) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsArticleContent'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,178 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑点赞文章' : '添加点赞文章'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="文章ID" name="articleId">
<a-input
allow-clear
placeholder="请输入文章ID"
v-model:value="form.articleId"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsArticleCount, updateCmsArticleCount } from '@/api/cms/cmsArticleCount';
import { CmsArticleCount } from '@/api/cms/cmsArticleCount/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsArticleCount | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsArticleCount>({
id: undefined,
articleId: undefined,
userId: undefined,
tenantId: undefined,
createTime: undefined,
cmsArticleCountId: undefined,
cmsArticleCountName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsArticleCountName: [
{
required: true,
type: 'string',
message: '请填写点赞文章名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsArticleCount : addCmsArticleCount;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,227 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsArticleCountId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsArticleCountEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsArticleCountEdit from './components/cmsArticleCountEdit.vue';
import { pageCmsArticleCount, removeCmsArticleCount, removeBatchCmsArticleCount } from '@/api/cms/cmsArticleCount';
import type { CmsArticleCount, CmsArticleCountParam } from '@/api/cms/cmsArticleCount/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsArticleCount[]>([]);
// 当前编辑数据
const current = ref<CmsArticleCount | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsArticleCount({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '主键ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '文章ID',
dataIndex: 'articleId',
key: 'articleId',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsArticleCountParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsArticleCount) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsArticleCount) => {
const hide = message.loading('请求中..', 0);
removeCmsArticleCount(row.cmsArticleCountId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsArticleCount(selection.value.map((d) => d.cmsArticleCountId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsArticleCount) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsArticleCount'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,178 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑点赞文章' : '添加点赞文章'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="文章ID" name="articleId">
<a-input
allow-clear
placeholder="请输入文章ID"
v-model:value="form.articleId"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsArticleLike, updateCmsArticleLike } from '@/api/cms/cmsArticleLike';
import { CmsArticleLike } from '@/api/cms/cmsArticleLike/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsArticleLike | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsArticleLike>({
id: undefined,
articleId: undefined,
userId: undefined,
tenantId: undefined,
createTime: undefined,
cmsArticleLikeId: undefined,
cmsArticleLikeName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsArticleLikeName: [
{
required: true,
type: 'string',
message: '请填写点赞文章名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsArticleLike : addCmsArticleLike;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,227 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsArticleLikeId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsArticleLikeEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsArticleLikeEdit from './components/cmsArticleLikeEdit.vue';
import { pageCmsArticleLike, removeCmsArticleLike, removeBatchCmsArticleLike } from '@/api/cms/cmsArticleLike';
import type { CmsArticleLike, CmsArticleLikeParam } from '@/api/cms/cmsArticleLike/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsArticleLike[]>([]);
// 当前编辑数据
const current = ref<CmsArticleLike | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsArticleLike({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '主键ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '文章ID',
dataIndex: 'articleId',
key: 'articleId',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsArticleLikeParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsArticleLike) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsArticleLike) => {
const hide = message.loading('请求中..', 0);
removeCmsArticleLike(row.cmsArticleLikeId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsArticleLike(selection.value.map((d) => d.cmsArticleLikeId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsArticleLike) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsArticleLike'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,252 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑组件' : '添加组件'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="组件标题" name="title">
<a-input
allow-clear
placeholder="请输入组件标题"
v-model:value="form.title"
/>
</a-form-item>
<a-form-item label="关联导航ID" name="navigationId">
<a-input
allow-clear
placeholder="请输入关联导航ID"
v-model:value="form.navigationId"
/>
</a-form-item>
<a-form-item label="组件类型" name="type">
<a-input
allow-clear
placeholder="请输入组件类型"
v-model:value="form.type"
/>
</a-form-item>
<a-form-item label="页面关键词" name="keywords">
<a-input
allow-clear
placeholder="请输入页面关键词"
v-model:value="form.keywords"
/>
</a-form-item>
<a-form-item label="页面描述" name="description">
<a-input
allow-clear
placeholder="请输入页面描述"
v-model:value="form.description"
/>
</a-form-item>
<a-form-item label="组件路径" name="path">
<a-input
allow-clear
placeholder="请输入组件路径"
v-model:value="form.path"
/>
</a-form-item>
<a-form-item label="组件图标" name="icon">
<a-input
allow-clear
placeholder="请输入组件图标"
v-model:value="form.icon"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsComponents, updateCmsComponents } from '@/api/cms/cmsComponents';
import { CmsComponents } from '@/api/cms/cmsComponents/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsComponents | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsComponents>({
id: undefined,
title: undefined,
navigationId: undefined,
type: undefined,
keywords: undefined,
description: undefined,
path: undefined,
icon: undefined,
userId: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
tenantId: undefined,
createTime: undefined,
cmsComponentsId: undefined,
cmsComponentsName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsComponentsName: [
{
required: true,
type: 'string',
message: '请填写组件名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsComponents : addCmsComponents;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,281 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsComponentsId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsComponentsEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsComponentsEdit from './components/cmsComponentsEdit.vue';
import { pageCmsComponents, removeCmsComponents, removeBatchCmsComponents } from '@/api/cms/cmsComponents';
import type { CmsComponents, CmsComponentsParam } from '@/api/cms/cmsComponents/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsComponents[]>([]);
// 当前编辑数据
const current = ref<CmsComponents | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsComponents({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '组件标题',
dataIndex: 'title',
key: 'title',
align: 'center',
},
{
title: '关联导航ID',
dataIndex: 'navigationId',
key: 'navigationId',
align: 'center',
},
{
title: '组件类型',
dataIndex: 'type',
key: 'type',
align: 'center',
},
{
title: '页面关键词',
dataIndex: 'keywords',
key: 'keywords',
align: 'center',
},
{
title: '页面描述',
dataIndex: 'description',
key: 'description',
align: 'center',
},
{
title: '组件路径',
dataIndex: 'path',
key: 'path',
align: 'center',
},
{
title: '组件图标',
dataIndex: 'icon',
key: 'icon',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsComponentsParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsComponents) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsComponents) => {
const hide = message.loading('请求中..', 0);
removeCmsComponents(row.cmsComponentsId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsComponents(selection.value.map((d) => d.cmsComponentsId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsComponents) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsComponents'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,300 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑页面管理记录表' : '添加页面管理记录表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="页面标题" name="name">
<a-input
allow-clear
placeholder="请输入页面标题"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="所属栏目ID" name="categoryId">
<a-input
allow-clear
placeholder="请输入所属栏目ID"
v-model:value="form.categoryId"
/>
</a-form-item>
<a-form-item label="页面关键词" name="keywords">
<a-input
allow-clear
placeholder="请输入页面关键词"
v-model:value="form.keywords"
/>
</a-form-item>
<a-form-item label="页面描述" name="description">
<a-input
allow-clear
placeholder="请输入页面描述"
v-model:value="form.description"
/>
</a-form-item>
<a-form-item label="缩列图" name="photo">
<a-input
allow-clear
placeholder="请输入缩列图"
v-model:value="form.photo"
/>
</a-form-item>
<a-form-item label="购买链接" name="buyUrl">
<a-input
allow-clear
placeholder="请输入购买链接"
v-model:value="form.buyUrl"
/>
</a-form-item>
<a-form-item label="页面样式" name="style">
<a-input
allow-clear
placeholder="请输入页面样式"
v-model:value="form.style"
/>
</a-form-item>
<a-form-item label="页面内容" name="content">
<a-input
allow-clear
placeholder="请输入页面内容"
v-model:value="form.content"
/>
</a-form-item>
<a-form-item label="是否开启布局" name="showLayout">
<a-input
allow-clear
placeholder="请输入是否开启布局"
v-model:value="form.showLayout"
/>
</a-form-item>
<a-form-item label="页面布局" name="layout">
<a-input
allow-clear
placeholder="请输入页面布局"
v-model:value="form.layout"
/>
</a-form-item>
<a-form-item label="上级id, 0是顶级" name="parentId">
<a-input
allow-clear
placeholder="请输入上级id, 0是顶级"
v-model:value="form.parentId"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="设为首页" name="home">
<a-input
allow-clear
placeholder="请输入设为首页"
v-model:value="form.home"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDesign, updateCmsDesign } from '@/api/cms/cmsDesign';
import { CmsDesign } from '@/api/cms/cmsDesign/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDesign | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDesign>({
pageId: undefined,
name: undefined,
categoryId: undefined,
keywords: undefined,
description: undefined,
photo: undefined,
buyUrl: undefined,
style: undefined,
content: undefined,
showLayout: undefined,
layout: undefined,
parentId: undefined,
userId: undefined,
home: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
cmsDesignId: undefined,
cmsDesignName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDesignName: [
{
required: true,
type: 'string',
message: '请填写页面管理记录表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsDesign : addCmsDesign;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,321 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsDesignId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsDesignEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsDesignEdit from './components/cmsDesignEdit.vue';
import { pageCmsDesign, removeCmsDesign, removeBatchCmsDesign } from '@/api/cms/cmsDesign';
import type { CmsDesign, CmsDesignParam } from '@/api/cms/cmsDesign/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsDesign[]>([]);
// 当前编辑数据
const current = ref<CmsDesign | null>(null);
// 多语言
const { locale } = useI18n();
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
where.lang = locale.value || undefined;
return pageCmsDesign({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'pageId',
key: 'pageId',
align: 'center',
width: 90,
},
{
title: '页面标题',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '所属栏目ID',
dataIndex: 'categoryId',
key: 'categoryId',
align: 'center',
},
{
title: '页面关键词',
dataIndex: 'keywords',
key: 'keywords',
align: 'center',
},
{
title: '页面描述',
dataIndex: 'description',
key: 'description',
align: 'center',
},
{
title: '缩列图',
dataIndex: 'photo',
key: 'photo',
align: 'center',
},
{
title: '购买链接',
dataIndex: 'buyUrl',
key: 'buyUrl',
align: 'center',
},
{
title: '页面样式',
dataIndex: 'style',
key: 'style',
align: 'center',
},
{
title: '页面内容',
dataIndex: 'content',
key: 'content',
align: 'center',
},
{
title: '是否开启布局',
dataIndex: 'showLayout',
key: 'showLayout',
align: 'center',
},
{
title: '页面布局',
dataIndex: 'layout',
key: 'layout',
align: 'center',
},
{
title: '上级id, 0是顶级',
dataIndex: 'parentId',
key: 'parentId',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '设为首页',
dataIndex: 'home',
key: 'home',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsDesignParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsDesign) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsDesign) => {
const hide = message.loading('请求中..', 0);
removeCmsDesign(row.cmsDesignId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsDesign(selection.value.map((d) => d.cmsDesignId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsDesign) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsDesign'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,240 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑设计征集' : '添加设计征集'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="" name="title">
<a-input
allow-clear
placeholder="请输入"
v-model:value="form.title"
/>
</a-form-item>
<a-form-item label="" name="content">
<a-input
allow-clear
placeholder="请输入"
v-model:value="form.content"
/>
</a-form-item>
<a-form-item
label=""
name="image">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseImage"
@del="onDeleteItem"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0已发布, 1待审核 2已驳回 3违规内容" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDesignCollect, updateCmsDesignCollect } from '@/api/cms/cmsDesignCollect';
import { CmsDesignCollect } from '@/api/cms/cmsDesignCollect/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDesignCollect | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDesignCollect>({
id: undefined,
title: undefined,
content: undefined,
image: undefined,
userId: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
cmsDesignCollectId: undefined,
cmsDesignCollectName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDesignCollectName: [
{
required: true,
type: 'string',
message: '请填写设计征集名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsDesignCollect : addCmsDesignCollect;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,269 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsDesignCollectId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsDesignCollectEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsDesignCollectEdit from './components/cmsDesignCollectEdit.vue';
import { pageCmsDesignCollect, removeCmsDesignCollect, removeBatchCmsDesignCollect } from '@/api/cms/cmsDesignCollect';
import type { CmsDesignCollect, CmsDesignCollectParam } from '@/api/cms/cmsDesignCollect/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsDesignCollect[]>([]);
// 当前编辑数据
const current = ref<CmsDesignCollect | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsDesignCollect({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '',
dataIndex: 'title',
key: 'title',
align: 'center',
},
{
title: '',
dataIndex: 'content',
key: 'content',
align: 'center',
},
{
title: '',
dataIndex: 'image',
key: 'image',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0已发布, 1待审核 2已驳回 3违规内容',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsDesignCollectParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsDesignCollect) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsDesignCollect) => {
const hide = message.loading('请求中..', 0);
removeCmsDesignCollect(row.cmsDesignCollectId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsDesignCollect(selection.value.map((d) => d.cmsDesignCollectId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsDesignCollect) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsDesignCollect'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,268 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑页面组件表' : '添加页面组件表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="关联导航ID" name="navigationId">
<a-input
allow-clear
placeholder="请输入关联导航ID"
v-model:value="form.navigationId"
/>
</a-form-item>
<a-form-item label="组件" name="title">
<a-input
allow-clear
placeholder="请输入组件"
v-model:value="form.title"
/>
</a-form-item>
<a-form-item label="组件标识" name="dictCode">
<a-input
allow-clear
placeholder="请输入组件标识"
v-model:value="form.dictCode"
/>
</a-form-item>
<a-form-item label="组件样式" name="styles">
<a-input
allow-clear
placeholder="请输入组件样式"
v-model:value="form.styles"
/>
</a-form-item>
<a-form-item label="卡片阴影显示时机" name="shadow">
<a-input
allow-clear
placeholder="请输入卡片阴影显示时机"
v-model:value="form.shadow"
/>
</a-form-item>
<a-form-item label="页面关键词" name="keywords">
<a-input
allow-clear
placeholder="请输入页面关键词"
v-model:value="form.keywords"
/>
</a-form-item>
<a-form-item label="页面描述" name="description">
<a-input
allow-clear
placeholder="请输入页面描述"
v-model:value="form.description"
/>
</a-form-item>
<a-form-item label="页面路由地址" name="path">
<a-input
allow-clear
placeholder="请输入页面路由地址"
v-model:value="form.path"
/>
</a-form-item>
<a-form-item label="缩列图" name="photo">
<a-input
allow-clear
placeholder="请输入缩列图"
v-model:value="form.photo"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDesignRecord, updateCmsDesignRecord } from '@/api/cms/cmsDesignRecord';
import { CmsDesignRecord } from '@/api/cms/cmsDesignRecord/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDesignRecord | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDesignRecord>({
id: undefined,
navigationId: undefined,
title: undefined,
dictCode: undefined,
styles: undefined,
shadow: undefined,
keywords: undefined,
description: undefined,
path: undefined,
photo: undefined,
userId: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
tenantId: undefined,
createTime: undefined,
cmsDesignRecordId: undefined,
cmsDesignRecordName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDesignRecordName: [
{
required: true,
type: 'string',
message: '请填写页面组件表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsDesignRecord : addCmsDesignRecord;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,293 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsDesignRecordId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsDesignRecordEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsDesignRecordEdit from './components/cmsDesignRecordEdit.vue';
import { pageCmsDesignRecord, removeCmsDesignRecord, removeBatchCmsDesignRecord } from '@/api/cms/cmsDesignRecord';
import type { CmsDesignRecord, CmsDesignRecordParam } from '@/api/cms/cmsDesignRecord/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsDesignRecord[]>([]);
// 当前编辑数据
const current = ref<CmsDesignRecord | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsDesignRecord({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '关联导航ID',
dataIndex: 'navigationId',
key: 'navigationId',
align: 'center',
},
{
title: '组件',
dataIndex: 'title',
key: 'title',
align: 'center',
},
{
title: '组件标识',
dataIndex: 'dictCode',
key: 'dictCode',
align: 'center',
},
{
title: '组件样式',
dataIndex: 'styles',
key: 'styles',
align: 'center',
},
{
title: '卡片阴影显示时机',
dataIndex: 'shadow',
key: 'shadow',
align: 'center',
},
{
title: '页面关键词',
dataIndex: 'keywords',
key: 'keywords',
align: 'center',
},
{
title: '页面描述',
dataIndex: 'description',
key: 'description',
align: 'center',
},
{
title: '页面路由地址',
dataIndex: 'path',
key: 'path',
align: 'center',
},
{
title: '缩列图',
dataIndex: 'photo',
key: 'photo',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsDesignRecordParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsDesignRecord) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsDesignRecord) => {
const hide = message.loading('请求中..', 0);
removeCmsDesignRecord(row.cmsDesignRecordId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsDesignRecord(selection.value.map((d) => d.cmsDesignRecordId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsDesignRecord) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsDesignRecord'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,244 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑设计征集报名' : '添加设计征集报名'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="" name="designId">
<a-input
allow-clear
placeholder="请输入"
v-model:value="form.designId"
/>
</a-form-item>
<a-form-item label="" name="name">
<a-input
allow-clear
placeholder="请输入"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="" name="phone">
<a-input
allow-clear
placeholder="请输入"
v-model:value="form.phone"
/>
</a-form-item>
<a-form-item label="" name="content">
<a-input
allow-clear
placeholder="请输入"
v-model:value="form.content"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="状态, 0已发布, 1待审核 2已驳回 3违规内容" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDesignSignUp, updateCmsDesignSignUp } from '@/api/cms/cmsDesignSignUp';
import { CmsDesignSignUp } from '@/api/cms/cmsDesignSignUp/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDesignSignUp | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDesignSignUp>({
id: undefined,
designId: undefined,
name: undefined,
phone: undefined,
content: undefined,
userId: undefined,
sortNumber: undefined,
comments: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
cmsDesignSignUpId: undefined,
cmsDesignSignUpName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDesignSignUpName: [
{
required: true,
type: 'string',
message: '请填写设计征集报名名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsDesignSignUp : addCmsDesignSignUp;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,275 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsDesignSignUpId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsDesignSignUpEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsDesignSignUpEdit from './components/cmsDesignSignUpEdit.vue';
import { pageCmsDesignSignUp, removeCmsDesignSignUp, removeBatchCmsDesignSignUp } from '@/api/cms/cmsDesignSignUp';
import type { CmsDesignSignUp, CmsDesignSignUpParam } from '@/api/cms/cmsDesignSignUp/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsDesignSignUp[]>([]);
// 当前编辑数据
const current = ref<CmsDesignSignUp | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsDesignSignUp({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '',
dataIndex: 'designId',
key: 'designId',
align: 'center',
},
{
title: '',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '',
dataIndex: 'phone',
key: 'phone',
align: 'center',
},
{
title: '',
dataIndex: 'content',
key: 'content',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '状态, 0已发布, 1待审核 2已驳回 3违规内容',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsDesignSignUpParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsDesignSignUp) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsDesignSignUp) => {
const hide = message.loading('请求中..', 0);
removeCmsDesignSignUp(row.cmsDesignSignUpId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsDesignSignUp(selection.value.map((d) => d.cmsDesignSignUpId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsDesignSignUp) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsDesignSignUp'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,260 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑文档管理记录表' : '添加文档管理记录表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="文档标题" name="title">
<a-input
allow-clear
placeholder="请输入文档标题"
v-model:value="form.title"
/>
</a-form-item>
<a-form-item label="上级目录" name="parentId">
<a-input
allow-clear
placeholder="请输入上级目录"
v-model:value="form.parentId"
/>
</a-form-item>
<a-form-item label="书籍ID" name="bookId">
<a-input
allow-clear
placeholder="请输入书籍ID"
v-model:value="form.bookId"
/>
</a-form-item>
<a-form-item label="可见性(public,private,protected)" name="visibility">
<a-input
allow-clear
placeholder="请输入可见性(public,private,protected)"
v-model:value="form.visibility"
/>
</a-form-item>
<a-form-item label="虚拟阅读量(仅用作展示)" name="virtualViews">
<a-input
allow-clear
placeholder="请输入虚拟阅读量(仅用作展示)"
v-model:value="form.virtualViews"
/>
</a-form-item>
<a-form-item label="实际阅读量" name="actualViews">
<a-input
allow-clear
placeholder="请输入实际阅读量"
v-model:value="form.actualViews"
/>
</a-form-item>
<a-form-item label="用户ID" name="userId">
<a-input
allow-clear
placeholder="请输入用户ID"
v-model:value="form.userId"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
<a-form-item label="修改时间" name="updateTime">
<a-input
allow-clear
placeholder="请输入修改时间"
v-model:value="form.updateTime"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDocs, updateCmsDocs } from '@/api/cms/cmsDocs';
import { CmsDocs } from '@/api/cms/cmsDocs/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDocs | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDocs>({
docsId: undefined,
title: undefined,
parentId: undefined,
bookId: undefined,
visibility: undefined,
virtualViews: undefined,
actualViews: undefined,
userId: undefined,
comments: undefined,
sortNumber: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
cmsDocsId: undefined,
cmsDocsName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDocsName: [
{
required: true,
type: 'string',
message: '请填写文档管理记录表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsDocs : addCmsDocs;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,287 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsDocsId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsDocsEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsDocsEdit from './components/cmsDocsEdit.vue';
import { pageCmsDocs, removeCmsDocs, removeBatchCmsDocs } from '@/api/cms/cmsDocs';
import type { CmsDocs, CmsDocsParam } from '@/api/cms/cmsDocs/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsDocs[]>([]);
// 当前编辑数据
const current = ref<CmsDocs | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsDocs({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '文档ID',
dataIndex: 'docsId',
key: 'docsId',
align: 'center',
width: 90,
},
{
title: '文档标题',
dataIndex: 'title',
key: 'title',
align: 'center',
},
{
title: '上级目录',
dataIndex: 'parentId',
key: 'parentId',
align: 'center',
},
{
title: '书籍ID',
dataIndex: 'bookId',
key: 'bookId',
align: 'center',
},
{
title: '可见性(public,private,protected)',
dataIndex: 'visibility',
key: 'visibility',
align: 'center',
},
{
title: '虚拟阅读量(仅用作展示)',
dataIndex: 'virtualViews',
key: 'virtualViews',
align: 'center',
},
{
title: '实际阅读量',
dataIndex: 'actualViews',
key: 'actualViews',
align: 'center',
},
{
title: '用户ID',
dataIndex: 'userId',
key: 'userId',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsDocsParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsDocs) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsDocs) => {
const hide = message.loading('请求中..', 0);
removeCmsDocs(row.cmsDocsId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsDocs(selection.value.map((d) => d.cmsDocsId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsDocs) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsDocs'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,228 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑书籍记录表' : '添加书籍记录表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="书籍名称" name="name">
<a-input
allow-clear
placeholder="请输入书籍名称"
v-model:value="form.name"
/>
</a-form-item>
<a-form-item label="书籍标识" name="code">
<a-input
allow-clear
placeholder="请输入书籍标识"
v-model:value="form.code"
/>
</a-form-item>
<a-form-item label="封面图" name="photo">
<a-input
allow-clear
placeholder="请输入封面图"
v-model:value="form.photo"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="文档内容" name="content">
<a-input
allow-clear
placeholder="请输入文档内容"
v-model:value="form.content"
/>
</a-form-item>
<a-form-item label="排序(数字越小越靠前)" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="状态, 0正常, 1冻结" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="0">显示</a-radio>
<a-radio :value="1">隐藏</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="是否删除, 0否, 1是" name="deleted">
<a-input
allow-clear
placeholder="请输入是否删除, 0否, 1是"
v-model:value="form.deleted"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDocsBook, updateCmsDocsBook } from '@/api/cms/cmsDocsBook';
import { CmsDocsBook } from '@/api/cms/cmsDocsBook/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDocsBook | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDocsBook>({
bookId: undefined,
name: undefined,
code: undefined,
photo: undefined,
comments: undefined,
content: undefined,
sortNumber: undefined,
status: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
cmsDocsBookId: undefined,
cmsDocsBookName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDocsBookName: [
{
required: true,
type: 'string',
message: '请填写书籍记录表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsDocsBook : addCmsDocsBook;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,263 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsDocsBookId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsDocsBookEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsDocsBookEdit from './components/cmsDocsBookEdit.vue';
import { pageCmsDocsBook, removeCmsDocsBook, removeBatchCmsDocsBook } from '@/api/cms/cmsDocsBook';
import type { CmsDocsBook, CmsDocsBookParam } from '@/api/cms/cmsDocsBook/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsDocsBook[]>([]);
// 当前编辑数据
const current = ref<CmsDocsBook | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsDocsBook({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'bookId',
key: 'bookId',
align: 'center',
width: 90,
},
{
title: '书籍名称',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '书籍标识',
dataIndex: 'code',
key: 'code',
align: 'center',
},
{
title: '封面图',
dataIndex: 'photo',
key: 'photo',
align: 'center',
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
align: 'center',
},
{
title: '文档内容',
dataIndex: 'content',
key: 'content',
align: 'center',
},
{
title: '排序(数字越小越靠前)',
dataIndex: 'sortNumber',
key: 'sortNumber',
align: 'center',
},
{
title: '状态, 0正常, 1冻结',
dataIndex: 'status',
key: 'status',
align: 'center',
},
{
title: '是否删除, 0否, 1是',
dataIndex: 'deleted',
key: 'deleted',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsDocsBookParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsDocsBook) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsDocsBook) => {
const hide = message.loading('请求中..', 0);
removeCmsDocsBook(row.cmsDocsBookId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsDocsBook(selection.value.map((d) => d.cmsDocsBookId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsDocsBook) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsDocsBook'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,178 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑文档内容记录表' : '添加文档内容记录表'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="文档ID" name="docsId">
<a-input
allow-clear
placeholder="请输入文档ID"
v-model:value="form.docsId"
/>
</a-form-item>
<a-form-item label="文档内容" name="content">
<a-input
allow-clear
placeholder="请输入文档内容"
v-model:value="form.content"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDocsContent, updateCmsDocsContent } from '@/api/cms/cmsDocsContent';
import { CmsDocsContent } from '@/api/cms/cmsDocsContent/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDocsContent | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDocsContent>({
id: undefined,
docsId: undefined,
content: undefined,
tenantId: undefined,
createTime: undefined,
cmsDocsContentId: undefined,
cmsDocsContentName: '',
status: 0,
comments: '',
sortNumber: 100
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDocsContentName: [
{
required: true,
type: 'string',
message: '请填写文档内容记录表名称',
trigger: 'blur'
}
]
});
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.image = data.path;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.image = '';
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
const saveOrUpdate = isUpdate.value ? updateCmsDocsContent : addCmsDocsContent;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
if(props.data.image){
images.value.push({
uid: uuid(),
url: props.data.image,
status: 'done'
})
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

View File

@@ -0,0 +1,227 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="cmsDocsContentId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<CmsDocsContentEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import CmsDocsContentEdit from './components/cmsDocsContentEdit.vue';
import { pageCmsDocsContent, removeCmsDocsContent, removeBatchCmsDocsContent } from '@/api/cms/cmsDocsContent';
import type { CmsDocsContent, CmsDocsContentParam } from '@/api/cms/cmsDocsContent/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<CmsDocsContent[]>([]);
// 当前编辑数据
const current = ref<CmsDocsContent | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 加载状态
const loading = ref(true);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
return pageCmsDocsContent({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'id',
key: 'id',
align: 'center',
width: 90,
},
{
title: '文档ID',
dataIndex: 'docsId',
key: 'docsId',
align: 'center',
},
{
title: '文档内容',
dataIndex: 'content',
key: 'content',
align: 'center',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: CmsDocsContentParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: CmsDocsContent) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
/* 删除单个 */
const remove = (row: CmsDocsContent) => {
const hide = message.loading('请求中..', 0);
removeCmsDocsContent(row.cmsDocsContentId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchCmsDocsContent(selection.value.map((d) => d.cmsDocsContentId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: CmsDocsContent) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'CmsDocsContent'
};
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,186 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="800"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '绑定域名' : '绑定域名'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
"
>
<a-form-item label="绑定域名" name="domain">
<a-input
allow-clear
placeholder="请输入已备案的域名"
v-model:value="form.domain"
/>
</a-form-item>
<a-form-item label="主机记录" name="hostName" v-if="isUpdate">
<a-input
allow-clear
placeholder="请输入主机记录"
disabled
v-model:value="form.hostName"
/>
</a-form-item>
<a-form-item label="记录值" name="hostValue" v-if="isUpdate">
<a-input
allow-clear
placeholder="请输入记录值"
disabled
v-model:value="form.hostValue"
/>
</a-form-item>
<!-- <a-form-item label="状态" name="status">-->
<!-- <a-radio-group v-model:value="form.status">-->
<!-- <a-radio :value="0">显示</a-radio>-->
<!-- <a-radio :value="1">隐藏</a-radio>-->
<!-- </a-radio-group>-->
<!-- </a-form-item>-->
<a-form-item label="排序号" v-if="isUpdate" name="sortNumber">
<a-input-number
:min="0"
:max="9999"
class="ele-fluid"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, uuid } from 'ele-admin-pro';
import { addCmsDomain, updateCmsDomain } from '@/api/cms/cmsDomain';
import { CmsDomain } from '@/api/cms/cmsDomain/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: CmsDomain | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 用户信息
const form = reactive<CmsDomain>({
id: undefined,
type: undefined,
domain: undefined,
hostName: undefined,
hostValue: undefined,
status: undefined,
sortNumber: 100,
websiteId: undefined,
appId: undefined,
userId: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
cmsDomainName: [
{
required: true,
type: 'string',
message: '请填写网站域名记录表名称',
trigger: 'blur'
}
]
});
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form,
websiteId: Number(localStorage.getItem('WebsiteId'))
};
const saveOrUpdate = isUpdate.value ? updateCmsDomain : addCmsDomain;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script>

View File

@@ -0,0 +1,42 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>绑定域名</span>
</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
const props = withDefaults(
defineProps<{
// 选中的角色
selection?: [];
}>(),
{}
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
};
watch(
() => props.selection,
() => {}
);
</script>

Some files were not shown because too many files have changed in this diff Show More