2025-06-08 20:18:35 +08:00

398 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-del="rowDel"
>
<template #menu-left>
<el-button type="primary" icon="Plus" @click="onAdd">新增</el-button>
<el-button type="success" icon="download" @click="onExport">导出</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
<template #search>
<div class="custom-search">
<div class="search-fields">
<div class="search-item">
<span>网格员姓名</span>
<el-input v-model="state.query.memberName" placeholder="网格员姓名" style="width: 200px; margin-right: 10px" />
</div>
<div class="search-item">
<AreaCascader
v-model:region-code="areaQuery.regionCode"
v-model:grid-id="areaQuery.gridId"
placeholder="选择行政区域与网格"
:show-separator="true"
:width="500"
/>
</div>
</div>
<div class="search-buttons">
<el-button type="primary" @click="triggerSearch">搜索</el-button>
<el-button @click="resetSearch">重置</el-button>
</div>
</div>
</template>
</avue-crud>
<el-dialog v-model="addDialogVisible" :title="isEdit ? '编辑网格员' : '新增网格员'" width="600px">
<el-form ref="addFormRef" :model="addForm" :rules="addFormRules" label-width="120px">
<el-form-item label="网格员姓名" prop="memberName">
<el-input v-model="addForm.memberName" style="width: 380px" />
</el-form-item>
<el-form-item label="" prop="gridId" label-width="0px">
<!-- 假设 AreaCascader 是行政区域-网格的级联选择组件 -->
<AreaCascader v-model:region-code="addForm.gridAreaCode" v-model:grid-id="addForm.gridId" split-rows label="" />
</el-form-item>
<el-form-item label="电话号码" prop="phone">
<el-input v-model="addForm.phone" style="width: 380px" />
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="addForm.note" type="textarea" style="width: 380px" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
// 请根据你的项目路径替换为实际的接口路径
import { GetGridList, GetMemberList, AddMember, UpdateMember, DeleteMember, ExportMember } from '@/apis/resource/member';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const crudRef = ref(null);
// 新增区域查询对象
const areaQuery = reactive({
regionCode: '',
gridId: '',
});
const state = reactive({
loading: false,
query: { current: 1, size: 10, memberName: '', gridAreaCode: '', gridId: '' },
form: {},
selection: [],
gridOptions: [],
options: {
...CRUD_OPTIONS,
addBtn: false,
searchBtn: false,
emptyBtn: false,
column: [
{
label: '网格员姓名',
prop: 'memberName',
},
{
label: '所属行政区域',
prop: 'gridAreaName',
width: 300,
},
{
label: '所属网格',
prop: 'gridId',
type: 'select',
width: 200,
dicData: [], // loadGridOptions 方法加载网格数据
props: {
label: 'gridName',
value: 'id',
},
},
{
label: '电话号码',
prop: 'phone',
},
{
label: '备注',
prop: 'note',
type: 'textarea',
span: 24,
rows: 3,
overHidden: true,
width: 200,
},
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => rowView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => rowEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => rowDel(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
// 新增弹窗相关
const addDialogVisible = ref(false);
const isEdit = ref(false);
const addFormRef = ref(null);
const addForm = reactive({
id: '',
memberName: '',
gridAreaCode: '',
gridId: '',
phone: '',
note: '',
});
const addFormRules = {
memberName: [{ required: true, message: '请输入网格员姓名', trigger: 'blur' }],
gridId: [{ required: true, message: '请选择所属网格', trigger: 'change' }],
phone: [
{ required: true, message: '请输入电话号码', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' },
],
};
const loadData = () => {
state.loading = true;
GetMemberList(state.query)
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
state.data = records;
state.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
});
};
loadData();
// 加载网格列表
const loadGridOptions = async () => {
try {
const res = await GetGridList(); // 获取足够多的网格数据
if (res.code === 200) {
state.options.column.find((item) => item.prop === 'gridId').dicData = res.data.records;
}
} catch (error) {
app.$message.error('获取网格列表失败');
}
};
loadGridOptions();
const currentChange = (current) => {
state.query.current = current;
loadData();
};
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
// 触发搜索
const triggerSearch = () => {
state.query.gridAreaCode = areaQuery.regionCode;
state.query.gridId = areaQuery.gridId;
state.query.current = 1;
loadData();
};
// 重置搜索
const resetSearch = () => {
state.query.memberName = '';
state.query.gridAreaCode = '';
state.query.gridId = '';
areaQuery.regionCode = '';
areaQuery.gridId = '';
state.query.current = 1;
loadData();
};
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
const selectionChange = (rows) => {
state.selection = rows;
};
const rowView = (row) => {
crudRef.value.rowView(row);
};
const rowEdit = (row) => {
isEdit.value = true;
Object.assign(addForm, row);
addDialogVisible.value = true;
};
// 打开新增弹窗
const onAdd = () => {
addDialogVisible.value = true;
// 清空表单
Object.keys(addForm).forEach((key) => {
addForm[key] = '';
});
};
// 提交表单
const submitForm = async () => {
if (!addFormRef.value) return;
try {
await addFormRef.value.validate();
if (isEdit.value) {
await UpdateMember(addForm);
app.$message.success('编辑网格员成功');
} else {
await AddMember(addForm);
app.$message.success('新增网格员成功');
}
addDialogVisible.value = false;
loadData();
} catch (error) {
app.$message.error(isEdit.value ? '编辑网格员失败' : '新增网格员失败');
}
};
const rowDel = (row, index, done) => {
if (isEmpty(row)) return;
app
.$confirm(`确认删除该网格员吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
DeleteMember(row.id) // 👈 只传 id
.then((res) => {
if (res.code === 200) {
app.$message.success('删除成功!');
loadData();
done?.(); // 若使用 Table 的 inline 操作支持回调
}
})
.catch((err) => app.$message.error(err.msg || '删除失败'));
})
.catch(() => {});
};
const onExport = () => {
if (isEmpty(state.data)) {
app.$message.error('当前暂时没有可供导出的数据!');
return;
}
state.loading = true;
// 仅发送有效的查询条件
const { current, size, ...query } = state.query;
ExportMember(query)
.then((res) => {
downloadFile(res, '网格员明细表.xlsx', 'blob');
app.$message.success('导出成功!');
})
.catch(() => app.$message.error('导出失败!'))
.finally(() => {
state.loading = false;
});
};
</script>
<style scoped lang="scss">
.custom-page {
padding: 20px;
.custom-search {
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 16px;
border-radius: 8px;
// background-color: #f5f7fa;
.search-fields {
display: flex;
flex-wrap: wrap;
gap: 16px;
width: 100%;
margin-bottom: 16px;
}
.search-item {
display: flex;
align-items: center;
gap: 8px;
.el-input,
.el-select,
.el-cascader {
width: 200px;
}
span {
font-size: 14px;
color: #606266;
align-self: center;
}
}
.search-buttons {
display: flex;
align-self: center;
gap: 12px;
margin-left: 8px;
}
}
}
</style>