| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- <script setup lang="ts">
- import { onMounted, reactive, ref } from 'vue'
- import { clientDownloadExcel, clientGet, clientPost } from '@/utils/request.ts'
- import type { FormInstance, UploadProps } from 'element-plus'
- import { ElForm, ElLoading, ElMessage, ElMessageBox } from 'element-plus'
- import { Delete, Download, Edit, Plus, Refresh, Search, Upload } from '@element-plus/icons-vue'
- // 假设 BaseResponse 和 PageType 在全局或 request.ts 中已定义
- interface BaseResponse {
- code: number
- msg: string
- }
- interface PageType<T> {
- records: T[]
- total: number
- size: number
- current: number
- pages: number
- }
- interface Property {
- id: string // 主键
- entName?: string // 企业(机构)名称
- uniCode?: string // 统一社会信用代码
- certificateIssuanceDate?: string // 发证日期
- certificateType?: string // 证书类型
- productName?: string // 产品名称及单元(主)
- certificateNo?: string // 证书编号
- deadlineDate?: string // 截止日期
- createTime?: string // 记录创建时间
- updateTime?: string // 记录最后更新时间
- updateBy?: string // 修改人
- createBy?: string // 创建人
- }
- interface PropertyOneResponse extends BaseResponse {
- data: Property
- }
- interface PropertyListResponse extends BaseResponse {
- data: PageType<Property>
- }
- interface AddProperty {
- entName?: string // 企业(机构)名称
- uniCode?: string // 统一社会信用代码
- certificateIssuanceDate?: string // 发证日期
- certificateType?: string // 证书类型
- productName?: string // 产品名称及单元(主)
- certificateNo?: string // 证书编号
- deadlineDate?: string // 截止日期
- }
- interface UpdateProperty {
- id?: string
- entName?: string // 企业(机构)名称
- uniCode?: string // 统一社会信用代码
- certificateIssuanceDate?: string // 发证日期
- certificateType?: string // 证书类型
- productName?: string // 产品名称及单元(主)
- certificateNo?: string // 证书编号
- deadlineDate?: string // 截止日期
- }
- // 响应式状态变量
- const tableData = ref<Property[]>([])
- const total = ref(0)
- const pageSize = ref(10)
- const pageNum = ref(1)
- const searchForm = reactive({
- entName: '',
- certificateType: '',
- certificateNo: '',
- })
- const dialogVisible = ref(false)
- const dialogTitle = ref('')
- const isEdit = ref(false)
- const formData = reactive<Property>({})
- const formRef = ref<FormInstance>()
- const selectedIds = ref<string[]>([])
- // 证书类型选项
- const certificateTypeOptions = [
- { label: '营业执照', value: '营业执照' },
- { label: '资质证书', value: '资质证书' },
- { label: '经营许可证', value: '经营许可证' },
- { label: '其他', value: '其他' },
- ]
- // 新增
- const add = async (data: AddProperty) => {
- const res = await clientPost<AddProperty, BaseResponse>('/equalificationCertificate/save', {
- ...data,
- })
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- return true
- }
- // 根据id获取数据
- const getById = async (id: string) => {
- const res = await clientGet<null, PropertyOneResponse>('/equalificationCertificate/getById/' + id)
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return null
- }
- return res.data
- }
- // 分页获取数据
- const getList = async () => {
- // 构建参数对象,只包含有值的搜索条件
- const params = {
- pageNum: pageNum.value,
- pageSize: pageSize.value,
- ...(searchForm.entName ? { entName: searchForm.entName } : {}),
- ...(searchForm.certificateType ? { certificateType: searchForm.certificateType } : {}),
- ...(searchForm.certificateNo ? { certificateNo: searchForm.certificateNo } : {}),
- }
- const res = await clientGet<
- {
- pageNum: number
- pageSize: number
- entName?: string
- certificateType?: string
- certificateNo?: string
- },
- PropertyListResponse
- >('/equalificationCertificate/findByPage', {
- params,
- })
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return
- }
- tableData.value = res.data.records
- total.value = res.data.total
- }
- // 批量删除
- const delBatch = async (ids: string[]) => {
- const res = await clientPost<string[], BaseResponse>(
- '/equalificationCertificate/deleteBatch',
- ids,
- )
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- return true
- }
- // 修改
- const update = async (data: UpdateProperty) => {
- const res = await clientPost<UpdateProperty, BaseResponse>('/equalificationCertificate/update', {
- ...data,
- })
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- return true
- }
- // 导出为excel
- const exportExcel = async () => {
- const loading = ElLoading.service({
- lock: true,
- text: '导出中,请稍候...',
- background: 'rgba(0, 0, 0, 0.1)',
- fullscreen: true,
- })
- try {
- // 构建参数对象,只包含有值的搜索条件
- const params = {
- ...(searchForm.entName ? { entName: searchForm.entName } : {}),
- ...(searchForm.certificateType ? { certificateType: searchForm.certificateType } : {}),
- ...(searchForm.certificateNo ? { certificateNo: searchForm.certificateNo } : {}),
- }
- await clientDownloadExcel('/equalificationCertificate/exportData', {
- params,
- })
- ElMessage.success('导出成功')
- } catch (error) {
- console.error('导出失败:', error)
- ElMessage.error('导出失败')
- } finally {
- loading.close()
- }
- }
- // excel导入
- const importExcel = async (file: File) => {
- const formData = new FormData()
- formData.append('file', file)
- const res = await clientPost<FormData, BaseResponse>(
- '/equalificationCertificate/importData',
- formData,
- {
- headers: {
- 'Content-Type': 'multipart/form-data',
- },
- },
- )
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- return true
- }
- // --- CRUD 操作和处理函数 ---
- const handleAdd = () => {
- isEdit.value = false
- dialogTitle.value = '新增资质证书'
- // 重置表单数据
- Object.keys(formData).forEach((key) => delete formData[key as keyof Property])
- dialogVisible.value = true
- }
- const handleEdit = async (row: Property) => {
- isEdit.value = true
- dialogTitle.value = '编辑资质证书'
- const data = await getById(row.id)
- if (data) {
- // 直接将后端返回的字符串赋值给 formData
- // el-date-picker 配合 value-format 可以直接处理字符串
- Object.assign(formData, data)
- dialogVisible.value = true
- }
- }
- const handleDelete = async (id: string) => {
- ElMessageBox.confirm('确定删除此条记录吗?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(async () => {
- const success = await delBatch([id])
- if (success) {
- getList()
- }
- })
- .catch(() => {
- ElMessage.info('已取消删除')
- })
- }
- const handleBatchDelete = () => {
- if (selectedIds.value.length === 0) {
- ElMessage.warning('请选择要删除的记录')
- return
- }
- ElMessageBox.confirm(`确定删除选中的 ${selectedIds.value.length} 条记录吗?`, '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(async () => {
- const success = await delBatch(selectedIds.value)
- if (success) {
- getList()
- }
- })
- .catch(() => {
- ElMessage.info('已取消批量删除')
- })
- }
- const handleSearch = () => {
- pageNum.value = 1 // 搜索时重置到第一页
- getList()
- }
- const handleResetSearch = () => {
- searchForm.entName = ''
- searchForm.certificateType = ''
- searchForm.certificateNo = ''
- pageNum.value = 1
- getList()
- }
- const handleConfirm = async () => {
- if (!formRef.value) return
- await formRef.value.validate(async (valid) => {
- if (valid) {
- let success = false
- const dataToSend = { ...formData }
- if (isEdit.value) {
- success = await update(dataToSend)
- } else {
- success = await add(dataToSend)
- }
- if (success) {
- dialogVisible.value = false
- getList()
- }
- } else {
- ElMessage.error('请检查表单填写')
- }
- })
- }
- const handleCurrentChange = (val: number) => {
- pageNum.value = val
- getList()
- }
- const handleSizeChange = (val: number) => {
- pageSize.value = val
- pageNum.value = 1 // 页大小改变时重置到第一页
- getList()
- }
- const handleSelectionChange = (selection: Property[]) => {
- selectedIds.value = selection.map((item) => item.id)
- }
- const handleUploadSuccess: UploadProps['onSuccess'] = async (response, uploadFile) => {
- if (response && response.code === 200) {
- ElMessage.success('文件导入成功')
- getList()
- } else {
- ElMessage.error(`文件导入失败: ${response?.msg || '未知错误'}`)
- }
- }
- const handleUploadError: UploadProps['onError'] = (error) => {
- ElMessage.error(`文件导入失败: ${error.message}`)
- }
- const handleBeforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
- const isExcel =
- rawFile.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
- rawFile.type === 'application/vnd.ms-excel'
- if (!isExcel) {
- ElMessage.error('导入文件只能是 Excel 格式!')
- return false
- }
- return true
- }
- const init = () => {
- getList()
- }
- onMounted(() => {
- init()
- })
- </script>
- <template>
- <div class="p-4">
- <h1 class="text-2xl font-bold mb-6">资质证书管理信息</h1>
- <!-- Search and Action Bar -->
- <div class="mb-6 p-4 bg-white rounded-lg shadow-sm flex flex-wrap items-center gap-4">
- <el-form :inline="true" :model="searchForm" class="flex-grow flex flex-wrap gap-x-4">
- <el-form-item label="企业名称">
- <el-input v-model="searchForm.entName" placeholder="输入企业名称" clearable />
- </el-form-item>
- <el-form-item label="证书类型">
- <el-select
- v-model="searchForm.certificateType"
- placeholder="请选择证书类型"
- style="width: 150px"
- >
- <el-option
- v-for="item in certificateTypeOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="证书编号">
- <el-input v-model="searchForm.certificateNo" placeholder="请输入证书编号" clearable />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
- <el-button :icon="Refresh" @click="handleResetSearch">重置</el-button>
- </el-form-item>
- </el-form>
- <div class="flex gap-2">
- <el-button type="primary" :icon="Plus" @click="handleAdd">新增</el-button>
- <el-button
- type="danger"
- :icon="Delete"
- @click="handleBatchDelete"
- :disabled="selectedIds.length === 0"
- >批量删除</el-button
- >
- <el-button type="success" :icon="Download" @click="exportExcel">导出</el-button>
- <el-upload
- class="inline-block ml-2"
- action="/api/equalificationCertificate/importData"
- :show-file-list="false"
- :on-success="handleUploadSuccess"
- :on-error="handleUploadError"
- :before-upload="handleBeforeUpload"
- :http-request="(options) => importExcel(options.file)"
- >
- <el-button type="info" :icon="Upload">导入</el-button>
- </el-upload>
- </div>
- </div>
- <!-- Table -->
- <div class="bg-white rounded-lg shadow-sm p-4 overflow-x-auto">
- <el-table
- :data="tableData"
- style="width: 100%"
- border
- @selection-change="handleSelectionChange"
- >
- <el-table-column type="selection" width="55" fixed="left" />
- <el-table-column prop="entName" label="企业(机构)名称" width="150" />
- <el-table-column prop="uniCode" label="统一社会信用代码" width="180" />
- <el-table-column prop="certificateType" label="证书类型" width="120">
- <template #default="{ row }">
- {{ row.certificateType || '-' }}
- </template>
- </el-table-column>
- <el-table-column prop="certificateNo" label="证书编号" width="150" />
- <el-table-column prop="certificateIssuanceDate" label="发证日期" width="150">
- <template #default="{ row }">
- {{
- row.certificateIssuanceDate
- ? new Date(row.certificateIssuanceDate).toLocaleDateString('zh-CN')
- : '-'
- }}
- </template>
- </el-table-column>
- <el-table-column prop="deadlineDate" label="截止日期" width="150">
- <template #default="{ row }">
- {{ row.deadlineDate ? new Date(row.deadlineDate).toLocaleDateString('zh-CN') : '-' }}
- </template>
- </el-table-column>
- <el-table-column prop="productName" label="产品名称及单元" width="200" />
- <el-table-column label="操作" width="185" fixed="right">
- <template #default="{ row }">
- <el-button :icon="Edit" size="small" @click="handleEdit(row)">编辑</el-button>
- <el-button type="danger" :icon="Delete" size="small" @click="handleDelete(row.id)"
- >删除</el-button
- >
- </template>
- </el-table-column>
- </el-table>
- <!-- Pagination -->
- <div class="mt-4 flex justify-end">
- <el-pagination
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- :current-page="pageNum"
- :page-sizes="[10, 20, 50, 100]"
- :page-size="pageSize"
- layout="total, sizes, prev, pager, next, jumper"
- :total="total"
- />
- </div>
- </div>
- <!-- Add/Edit Dialog -->
- <el-dialog
- v-model="dialogVisible"
- :title="dialogTitle"
- width="800px"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- >
- <el-form
- :model="formData"
- ref="formRef"
- label-width="120px"
- class="grid grid-cols-1 md:grid-cols-2 gap-4"
- >
- <el-form-item
- label="企业(机构)名称"
- prop="entName"
- :rules="[{ required: true, message: '请输入企业(机构)名称', trigger: 'blur' }]"
- >
- <el-input v-model="formData.entName" placeholder="请输入企业(机构)名称" />
- </el-form-item>
- <el-form-item label="统一社会信用代码" prop="uniCode">
- <el-input v-model="formData.uniCode" placeholder="请输入统一社会信用代码" />
- </el-form-item>
- <el-form-item
- label="证书类型"
- prop="certificateType"
- :rules="[{ required: true, message: '请选择证书类型', trigger: 'change' }]"
- >
- <el-select
- v-model="formData.certificateType"
- placeholder="请选择证书类型"
- style="width: 100%"
- >
- <el-option
- v-for="item in certificateTypeOptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </el-form-item>
- <el-form-item
- label="证书编号"
- prop="certificateNo"
- :rules="[{ required: true, message: '请输入证书编号', trigger: 'blur' }]"
- >
- <el-input v-model="formData.certificateNo" placeholder="请输入证书编号" />
- </el-form-item>
- <el-form-item label="发证日期" prop="certificateIssuanceDate">
- <el-date-picker
- v-model="formData.certificateIssuanceDate"
- type="date"
- placeholder="选择发证日期"
- value-format="YYYY-MM-DD"
- style="width: 100%"
- />
- </el-form-item>
- <el-form-item label="截止日期" prop="deadlineDate">
- <el-date-picker
- v-model="formData.deadlineDate"
- type="date"
- placeholder="选择截止日期"
- value-format="YYYY-MM-DD"
- style="width: 100%"
- />
- </el-form-item>
- <el-form-item label="产品名称及单元" prop="productName" class="md:col-span-2">
- <el-input
- v-model="formData.productName"
- placeholder="请输入产品名称及单元"
- type="textarea"
- :rows="2"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleConfirm">确定</el-button>
- </span>
- </template>
- </el-dialog>
- </div>
- </template>
- <style scoped>
- /* Add any specific styles here if needed, otherwise UnoCSS handles most */
- </style>
|