| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- <template>
- <el-card shadow="never" :body-style="{ height: 'calc(100vh - 260px)' }">
- <el-scrollbar>
- <PlusForm
- v-model="props.selProject"
- class="w-[960px] m-auto"
- :columns="projectColumns"
- :rules="rules"
- label-position="right"
- @change="handleChange"
- @submit="handleSubmit"
- @submit-error="handleSubmitError"
- @reset="handleReset"
- >
- <template #footer="{ handleSubmit }">
- <div>
- <!-- <el-button
- v-if="props.swEditable"
- @click="dlgImportExcelVisible = true"
- >
- 从Excel导入
- </el-button> -->
- <el-button @click="handleExport"> 导出到Excel </el-button>
- <el-button
- v-if="props.swEditable"
- type="primary"
- @click="handleSubmit"
- >提交</el-button
- >
- </div>
- </template>
- <template #plus-field-id>
- <p label="ID" :content="props.selProject.id">
- {{ props.selProject.id }}
- </p>
- </template>
- <template #plus-field-name>
- <p label="name" :content="props.selProject.name">
- {{ props.selProject.name }}
- </p>
- </template>
- <template #plus-field-version>
- <p label="version" :content="props.selProject.version">
- {{ props.selProject.version }}
- </p>
- </template>
- <template #plus-field-status>
- <el-select v-if="swEditable" v-model="props.selProject.status">
- <el-option label="未运行" :value="0"></el-option>
- <el-option label="运行中" :value="1"></el-option>
- <el-option label="已完成" :value="2"></el-option>
- </el-select>
- <el-tag
- v-else
- :type="
- props.selProject.status == 0
- ? 'info'
- : props.selProject.status == 1
- ? 'primary'
- : 'success'
- "
- >
- {{
- props.selProject.status == 0
- ? "未运行"
- : props.selProject.status == 1
- ? "运行中"
- : "已完成"
- }}
- </el-tag>
- </template>
- <template #plus-field-locations>
- <!-- <div style="margin-bottom: 10px">
- <el-button v-if="swEditable" @click="onAddLocation">
- 添加区域
- </el-button>
- </div> -->
- <pure-table
- align-whole="center"
- :header-cell-style="{
- background: 'var(--el-fill-color-light)',
- color: 'var(--el-text-color-primary)'
- }"
- :columns="locationColumns"
- :table-key="tableKey"
- border
- showOverflowTooltip
- :data="locationDataList"
- >
- <template #operation="{ row, index }">
- <div v-if="swEditable && !locationEditMap[index]?.editable">
- <!-- <el-button
- class="reset-margin"
- link
- type="primary"
- :icon="useRenderIcon(Delete)"
- @click="onDelLocation(row)"
- >
- 删除
- </el-button>
- <el-button
- class="reset-margin"
- link
- type="primary"
- @click="onEditLocation(row, index)"
- :icon="useRenderIcon(EditFill)"
- >
- 修改
- </el-button> -->
- </div>
- <div v-if="swEditable && locationEditMap[index]?.editable">
- <el-button
- class="reset-margin"
- link
- type="primary"
- @click="onSaveLocation(index)"
- :icon="useRenderIcon(SaveFill)"
- >
- 保存
- </el-button>
- <el-button
- class="reset-margin"
- link
- @click="onCancelLocation(index)"
- :icon="useRenderIcon(Close)"
- >
- 取消
- </el-button>
- </div>
- </template>
- </pure-table>
- </template>
- <template #plus-field-persons>
- <!-- <div style="margin-bottom: 10px">
- <el-button v-if="swEditable" @click="onAddPerson">
- 添加人员
- </el-button>
- </div> -->
- <pure-table
- align-whole="center"
- :header-cell-style="{
- background: 'var(--el-fill-color-light)',
- color: 'var(--el-text-color-primary)'
- }"
- :columns="personColumns"
- :table-key="tableKey"
- border
- showOverflowTooltip
- :data="personDataList"
- >
- <template #operation="{ row, index }">
- <div v-if="swEditable && !personEditMap[index]?.editable">
- <!-- <el-button
- class="reset-margin"
- link
- type="primary"
- :icon="useRenderIcon(Delete)"
- @click="onDelPerson(row)"
- >
- 删除
- </el-button>
- <el-button
- class="reset-margin"
- link
- type="primary"
- @click="onEditPerson(row, index)"
- :icon="useRenderIcon(EditFill)"
- >
- 修改
- </el-button> -->
- </div>
- <div v-if="swEditable && personEditMap[index]?.editable">
- <el-button
- class="reset-margin"
- link
- type="primary"
- @click="onSavePerson(index)"
- :icon="useRenderIcon(SaveFill)"
- >
- 保存
- </el-button>
- <el-button
- class="reset-margin"
- link
- @click="onCancelPerson(index)"
- :icon="useRenderIcon(Close)"
- >
- 取消
- </el-button>
- </div>
- </template>
- </pure-table>
- </template>
- </PlusForm>
- </el-scrollbar>
- <el-dialog
- v-model="dlgImportExcelVisible"
- title="Excel导入"
- width="60%"
- :before-close="handleDlgClose"
- destroy-on-close
- top="6vh"
- >
- <importExcel
- ref="importExcelRef"
- @onExcelDataProject="onExcelDataProject"
- />
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="dlgImportExcelVisible = false">取消</el-button>
- <el-button type="primary" @click="handleDlgConfirm"> 导入 </el-button>
- </span>
- </template>
- </el-dialog>
- </el-card>
- </template>
- <script setup lang="ts">
- import { ref, onMounted } from "vue";
- // https://plus-pro-components.com/components/form.html
- import "plus-pro-components/es/components/form/style/css";
- import { useRenderIcon } from "@/components/ReIcon/src/hooks";
- import {
- type PlusColumn,
- type FieldValues,
- PlusForm
- } from "plus-pro-components";
- import { ElNotification, ElMessageBox } from "element-plus";
- import { text } from "stream/consumers";
- import AddFill from "~icons/ep/plus";
- import Delete from "~icons/ep/delete";
- import EditFill from "~icons/ep/edit";
- import SaveFill from "~icons/bi/save";
- import Close from "~icons/ep/close";
- import Project from "@/model/project.js";
- import { useLocationColumns } from "./locationColumns";
- import { usePersonColumns } from "./personColumns";
- import ImportExcel from "./importExcel.vue";
- import * as XLSX from "xlsx";
- const dlgImportExcelVisible = ref(false);
- const excelData = ref(null);
- const props = defineProps({
- // 字符串类型
- selProject: {
- type: Project,
- default: new Project()
- },
- swEditable: {
- type: Boolean,
- default: true
- }
- });
- const emit = defineEmits(["onSubmitData"]);
- const {
- locationEditMap,
- locationColumns,
- locationDataList,
- onEditLocation,
- onSaveLocation,
- onCancelLocation,
- onAddLocation,
- onDelLocation
- } = useLocationColumns();
- const {
- personEditMap,
- personColumns,
- personDataList,
- onEditPerson,
- onSavePerson,
- onCancelPerson,
- onAddPerson,
- onDelPerson
- } = usePersonColumns();
- const tableKey = ref<string>("");
- const state = ref<FieldValues>({
- status: "1",
- name: "",
- rate: 4,
- progress: 100,
- switch: true,
- time: new Date().toString(),
- endTime: []
- });
- const rules = {
- id: [
- {
- required: true,
- message: "请输入ID"
- }
- ]
- };
- const projectColumns: PlusColumn[] = [
- {
- label: "ID",
- width: 800,
- prop: "id",
- valueType: "input",
- tooltip: "ID必须填正整数",
- colProps: {
- span: 8
- }
- },
- {
- label: "项目名称",
- width: 800,
- prop: "name",
- valueType: "input",
- colProps: {
- span: 8
- }
- },
- {
- label: "版本",
- width: 800,
- prop: "version",
- valueType: "input",
- tooltip: "版本必须是正整数",
- colProps: {
- span: 8
- }
- },
- {
- label: "区域",
- width: 800,
- prop: "locations",
- valueType: "copy"
- },
- {
- label: "人员",
- width: 800,
- prop: "persons",
- valueType: "copy"
- },
- {
- label: "状态",
- width: 800,
- prop: "status",
- valueType: "select",
- options: [
- {
- label: "未运行",
- value: 0,
- color: "gray"
- },
- {
- label: "运行中",
- value: 1,
- color: "blue"
- },
- {
- label: "已完成",
- value: 2,
- color: "green"
- }
- ]
- }
- ];
- const handleChange = (values: FieldValues, prop: PlusColumn) => {
- console.log(values, prop, "change");
- };
- const handleSubmit = (values: FieldValues) => {
- console.log(values, "submit");
- props.selProject.locations = locationDataList.value;
- props.selProject.persons = personDataList.value;
- emit("onSubmitData", props.selProject);
- };
- const handleSubmitError = (err: any) => {
- console.log(err, "err");
- };
- const handleReset = () => {
- console.log("handleReset");
- };
- const handleExport = () => {
- try {
- const project = props.selProject;
- // 0. 创建工作簿
- const workbook = XLSX.utils.book_new();
- // 1-1. 准备 Proj参数 数据
- let exportData = [
- { name: "项目ID", value: project.id },
- { name: "项目名称", value: project.name },
- { name: "项目版本", value: project.version }
- ];
- // 1-2. 创建工作表
- let worksheet = XLSX.utils.json_to_sheet(exportData);
- // 1-3. 计算列宽(关键步骤)
- let header = Object.keys(exportData[0]);
- let cols = header.map(key => {
- // 计算表头文字长度
- let maxLength = key.length * 2;
- // 计算数据列最大文字长度
- exportData.forEach(row => {
- const value = String(row[key] || "");
- // 中文字符宽度约为英文字符的2倍,这里做特殊处理
- const length = value.replace(/[^\x00-\xff]/g, "aa").length;
- if (length > maxLength) {
- maxLength = length;
- }
- });
- // 增加缓冲宽度(避免内容太满)
- return { width: maxLength + 2 };
- });
- // 设置列宽配置
- worksheet["!cols"] = cols;
- // 1-4. 创建工作表
- XLSX.utils.book_append_sheet(workbook, worksheet, "项目");
- // 2-1. 准备 Proj参数 数据
- exportData = project.locations.map(item => ({
- id: item.id,
- name: item.name,
- cx: item.center.x,
- cy: item.center.y
- }));
- // 2-2. 创建工作表
- worksheet = XLSX.utils.json_to_sheet(exportData);
- // 2-3. 计算列宽(关键步骤)
- header = Object.keys(exportData[0]);
- cols = header.map(key => {
- // 计算表头文字长度
- let maxLength = key.length * 2;
- // 计算数据列最大文字长度
- exportData.forEach(row => {
- const value = String(row[key] || "");
- // 中文字符宽度约为英文字符的2倍,这里做特殊处理
- const length = value.replace(/[^\x00-\xff]/g, "aa").length;
- if (length > maxLength) {
- maxLength = length;
- }
- });
- // 增加缓冲宽度(避免内容太满)
- return { width: maxLength + 2 };
- });
- // 设置列宽配置
- worksheet["!cols"] = cols;
- // 2-4. 创建工作表
- XLSX.utils.book_append_sheet(workbook, worksheet, "区域");
- // 3-1. 准备 Proj参数 数据
- exportData = project.persons.map(item => ({
- id: item.id,
- name: item.name,
- job: item.job,
- phone: item.phone,
- locId: item.locId,
- tagId: item.tagId
- }));
- // 3-2. 创建工作表
- worksheet = XLSX.utils.json_to_sheet(exportData);
- // 3-3. 计算列宽(关键步骤)
- header = Object.keys(exportData[0]);
- cols = header.map(key => {
- // 计算表头文字长度
- let maxLength = key.length * 2;
- // 计算数据列最大文字长度
- exportData.forEach(row => {
- const value = String(row[key] || "");
- // 中文字符宽度约为英文字符的2倍,这里做特殊处理
- const length = value.replace(/[^\x00-\xff]/g, "aa").length;
- if (length > maxLength) {
- maxLength = length;
- }
- });
- // 增加缓冲宽度(避免内容太满)
- return { width: maxLength + 2 };
- });
- // 设置列宽配置
- worksheet["!cols"] = cols;
- // 3-4. 创建工作表
- XLSX.utils.book_append_sheet(workbook, worksheet, "人员");
- // 4. 生成Excel文件并下载
- const fileName = `项目_${project.name}_${new Date().getTime()}.xlsx`;
- XLSX.writeFile(workbook, fileName);
- } catch (error) {
- console.error("导出失败:", error);
- ElNotification.error({
- message: "导出失败,请重试"
- });
- }
- };
- const handleDlgConfirm = () => {
- // 将导入的数据合并到dataList
- tableKey.value += 1;
- dlgImportExcelVisible.value = false;
- props.selProject.init(excelData.value);
- init();
- };
- const handleDlgClose = (done: () => void) => {
- ElMessageBox.confirm("确定要关闭这个对话框吗?")
- .then(() => {
- done();
- })
- .catch(() => {
- // catch error
- });
- };
- const onExcelDataProject = (data: Project) => {
- console.log("onExcelDataProject", data);
- excelData.value = data;
- };
- const init = () => {
- locationDataList.value = props.selProject.locations;
- personDataList.value = props.selProject.persons;
- tableKey.value = props.selProject.id;
- };
- onMounted(() => {
- init();
- });
- </script>
|