feat:采收与赋码管理

This commit is contained in:
wangzenghua 2025-02-10 06:59:30 +00:00
parent c9829c6a5c
commit 182b97b336
16 changed files with 833 additions and 227 deletions

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2022-02-23 21:12:37
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-02-07 14:38:05
* @LastEditTime: 2025-02-10 14:47:12
*/
import lodash from 'lodash';
import dayjs from 'dayjs';
@ -291,43 +291,57 @@ export const obj2Param = (json) => {
* @returns
*/
export const getAssetsFile = (url) => {
return new URL(`../assets/images/${url}`, import.meta.url);
};
/**
* @Title: a链接方式文件下载
* @param {void} content:
* @param {void} fileName:
* @return {void}
*/
export const downloadLink = (url, fileName) => {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = url;
elink.target = '_blank';
elink.click();
elink.remove();
return new URL(`../assets/${url}`, import.meta.url);
};
/**
* @Title: 下载文件
* @param {void} content:
* @param {void} url:
* @param {void} fileName:
* @param {void} fileType:
* @return {void}
*/
export const downloadFile = (content, fileName) => {
const blob = new Blob([content]);
export const downloadFile = async (url, fileName, fileType) => {
let blob = null;
try {
switch (fileType) {
case 'image': {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = url;
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
blob = await new Promise((resolve) => {
canvas.toBlob(resolve, 'image/jpeg');
});
break;
}
case 'blob': {
blob = new Blob([url]);
break;
}
}
if ('download' in document.createElement('a')) {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
elink.href = blob ? URL.createObjectURL(blob) : url;
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
blob && URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
} else {
navigator.msSaveBlob(blob, fileName);
}
} catch (error) {
console.error('下载出错:', error);
}
};
/**
* @Title 模拟休眠

View File

@ -6,15 +6,14 @@ import vueSetupExtend from 'vite-plugin-vue-setup-extend';
import compression from 'vite-plugin-compression';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import { createHtmlPlugin } from 'vite-plugin-html';
// import { viteMockServe } from 'vite-plugin-mock';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import postcssImport from 'postcss-import';
import autoprefixer from 'autoprefixer';
// import postCssPxToRem from 'postcss-pxtorem';
import { resolve } from 'path';
export default defineConfig(({ command, mode }) => {
console.log('vite.config.js', command, mode, loadEnv(mode, process.cwd()));
const {
VITE_PORT,
VITE_APP_NAME,
@ -77,12 +76,6 @@ export default defineConfig(({ command, mode }) => {
autoprefixer({
overrideBrowserslist: ['> 1%', 'last 2 versions'],
}),
// postCssPxToRem({
// rootValue: 192,
// selectorBlackList: [],
// propList: ['*'],
// exclude: /node_modules/i,
// }),
],
},
},
@ -101,7 +94,7 @@ export default defineConfig(({ command, mode }) => {
include: ['src/**/*.ts', 'src/**/*.vue', 'src/*.ts', 'src/*.vue'],
}),
Components({
dirs: ['../global/components', 'src/components'],
dirs: ['src/components'],
extensions: ['vue', 'js', 'jsx', 'ts', 'tsx'],
resolvers: [],
}),
@ -114,12 +107,6 @@ export default defineConfig(({ command, mode }) => {
iconDirs: [resolve(process.cwd(), 'src/assets/svgs')],
symbolId: 'icon-[name]',
}),
// viteMockServe({
// mockPath: 'src/mock',
// watchFiles: true,
// localEnabled: command === 'dev',
// prodEnabled: false,
// }),
],
};
if (mode === 'production') {

View File

@ -3,10 +3,10 @@
* @Author: zenghua.wang
* @Date: 2022-02-23 21:12:37
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-01-25 17:05:12
* @LastEditTime: 2025-02-10 14:45:53
*/
import lodash from 'lodash';
import moment from 'moment';
import dayjs from 'dayjs';
import { Base64 } from 'js-base64';
/**
@ -90,7 +90,7 @@ export const setDefaultOption = (options, prop, defaultVal) => {
return options[prop] === undefined ? defaultVal : options.prop;
};
/**
* 设置字典值
* @Title 设置字典值
* @param {*} columns
* @param {*} key
* @param {*} data
@ -107,7 +107,7 @@ export const setDicData = (columns, key, data = []) => {
}
};
/**
* 求字段lable
* @Title 求字段lable
* @param {*} tree
* @returns
*/
@ -124,7 +124,35 @@ export const setDicLabel = (dicData, value) => {
return label;
};
/**
* 加密
* @Title 数组交集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const intersectionArray = (arr1 = [], arr2 = []) => {
return arr1.filter((item) => arr2.includes(item));
};
/**
* @Title 数组并集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const unionArray = (arr1 = [], arr2 = []) => {
return Array.from(new Set([...arr1, ...arr2]));
};
/**
* @Title 数组差集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const differenceArray = (arr1 = [], arr2 = []) => {
const s = new Set(arr2);
return arr1.filter((x) => !s.has(x));
};
/**
* @Title 加密
* @param {*} n
* @returns
*/
@ -146,7 +174,7 @@ export const encode = (n, flag = false) => {
return n;
};
/**
* 解密
* @Title 解密
* @param {*} e
* @returns
*/
@ -190,6 +218,22 @@ export const imageToBase64 = (file) => {
export const bufferToBase64 = (buffer) => {
return 'data:image/jpeg;base64,' + window.btoa(new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), ''));
};
/**
* @Title blob转json
* @param {*} file
* @returns
*/
export const blobToJSON = (blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(blob, 'utf-8');
reader.onload = () => {
const res = !isEmpty(reader.result) ? JSON.parse(reader.result) : reader.result;
resolve(res);
};
reader.onerror = reject;
});
};
/**
* @Title 将array转化为树
* @param tree
@ -227,43 +271,77 @@ export const getUrlQuery = (name) => {
const usp = new URLSearchParams(search);
return usp.get(name);
};
/**
* @Title 将Object参数转换为字符串
* @param {*} json
* @returns
*/
export const obj2Param = (json) => {
if (!json) return '';
return Object.keys(json)
.map((key) => {
if (isEmpty(json[key])) return '';
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
})
.join('&');
};
/**
* @Title 获取静态资源文件
* @param {*} url
* @returns
*/
export const getAssetsFile = (url) => {
return new URL(`../assets/images/${url}`, import.meta.url);
return new URL(`../assets/${url}`, import.meta.url);
};
/**
* @Title 替换图片url字段值
* @param {*} url
* @returns
*/
export const setUploadField = (url) => {
if (isEmpty(url) || url.includes('http')) return null;
return url;
};
/**
* @Title: a链接方式文件下载
* @param {void} content:
* @Title: 下载文件
* @param {void} url:
* @param {void} fileName:
* @param {void} fileType:
* @return {void}
*/
export const dowloadLink = (content, fileName) => {
const blob = new Blob([content]);
export const downloadFile = async (url, fileName, fileType) => {
let blob = null;
try {
switch (fileType) {
case 'image': {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = url;
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
blob = await new Promise((resolve) => {
canvas.toBlob(resolve, 'image/jpeg');
});
break;
}
case 'blob': {
blob = new Blob([url]);
break;
}
}
if ('download' in document.createElement('a')) {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
elink.href = blob ? URL.createObjectURL(blob) : url;
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
blob && URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
} else {
navigator.msSaveBlob(blob, fileName);
}
} catch (error) {
console.error('下载出错:', error);
}
};
/**
* @Title 模拟休眠
@ -275,20 +353,106 @@ export const sleep = (duration = 0) =>
setTimeout(resolve, duration);
});
/**
* 日期格式化
* @Title 创建id
* @param {*} prefix
* @returns
*/
export const createId = (prefix) => {
const val = Date.now() + Math.ceil(Math.random() * 99999);
return isEmpty(prefix) ? val : prefix + '-' + val;
};
/**
* @Title 生成数据
* @param {*} duration
* @returns
*/
export const mockData = (item = {}, len = 1) => {
const list = [];
for (let i = 0; i < len; i++) {
let temp = { ...item, id: createId() };
list.push(temp);
}
return list;
};
/**
* @Title 日期格式化
* @param {*} date
* @param {*} format
* @returns
*/
export const dateFormat = (datetime, type = 'yyyy-MM-dd') => {
return moment(datetime).format(type);
export const dateFormat = (datetime, formater = 'YYYY-MM-DD hh:mm:ss') => {
if (datetime instanceof Date || datetime) {
return dayjs(datetime).format(formater);
} else {
return null;
}
};
/**
* 上日///
* @Title 字符串转日期
* @param {*} str
* @returns
*/
export const lastDate = (last = 0, date = 'month', type = 'yyyy-MM-dd') => {
if (date === 'day') {
return moment().subtract(last, 'day').endOf('day').format(type);
}
return moment().subtract(last, date).format(type);
export const toDate = (str) => {
return !isEmpty(str) ? dayjs(str) : dayjs();
};
/**
* @Title 字符串转日期
* @param {*} str
* @returns
*/
export const getDate = (num, type, formater = 'YYYY-MM-DD', start = true) => {
const date = dayjs().subtract(num, type);
return start ? date.startOf(type).format(formater) : date.endOf(type).format(formater);
};
/**
* @Title: 获取时间差
* @param start
* @param end
* @param type
* @returns
*/
export const getDiffTime = (start, end, type) => {
const startTime = dayjs(start);
const endTime = dayjs(end);
const duration = endTime.diff(startTime);
let diff = 0;
switch (type) {
case 'DD': {
diff = duration / (1000 * 60 * 60 * 24);
break;
}
case 'HH': {
diff = duration / (1000 * 60 * 60);
break;
}
case 'mm': {
diff = duration / (1000 * 60);
break;
}
}
return Math.round(diff);
};
/**
* @Title: 开始日期
* @param last
* @param type
* @param formater
* @returns
*/
export const startDate = (num, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss') => {
if (num === 'now') return dayjs().format(formater);
if (typeof num === 'string') return dayjs(num).startOf(type).format(formater);
return num === 0 ? dayjs().startOf(type).format(formater) : dayjs().subtract(num, type).startOf(type).format(formater);
};
/**
* @Title: 结束日期
* @param num
* @param type
* @param formater
* @returns
*/
export const endDate = (num = 0, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss') => {
if (num === 'now') return dayjs().format(formater);
if (typeof num === 'string') return dayjs(num).endOf(type).format(formater);
return num === 0 ? dayjs().endOf(type).format(formater) : dayjs().subtract(num, type).endOf(type).format(formater);
};

View File

@ -2,10 +2,10 @@
VITE_PORT = 9528
VITE_MODE = 'DEV'
VITE_APP_NAME = 'sub-government-affairs-service'
VITE_APP_BASE_API = '/traceApis'
VITE_APP_BASE_API = '/apis'
VITE_APP_BASE_URL = 'http://192.168.18.99:8080'
VITE_APP_UPLOAD_API = '/uploadApis'
VITE_APP_UPLOAD_URL = 'http://192.168.18.99:9300'
VITE_APP_DICDATA_API = '/dicDataApis'
VITE_APP_DICDATA_URL = 'http://192.168.18.99:99/stage-api'
VITE_APP_SYSTEM_API = '/systemApis'
VITE_APP_SYSTEM_URL = 'http://192.168.18.99:99/stage-api'

View File

@ -3,7 +3,7 @@ import request from '@/utils/axios';
/**
* @Title: 列表
*/
export function GetEntityList(params) {
export function GetEntityList(params = {}) {
return request('/trace/code/farmMange/page', {
method: 'GET',
params,
@ -13,7 +13,7 @@ export function GetEntityList(params) {
/**
* @Title: 新增
*/
export function AddEntity(data) {
export function AddEntity(data = {}) {
return request('/trace/code/farmMange/save', {
method: 'POST',
data,
@ -23,7 +23,7 @@ export function AddEntity(data) {
/**
* @Title: 修改
*/
export function UpdateEntity(data) {
export function UpdateEntity(data = {}) {
return request('/trace/code/farmMange/edit', {
method: 'PUT',
data,
@ -33,7 +33,7 @@ export function UpdateEntity(data) {
/**
* @Title: 删除
*/
export function DeleteEntity(params) {
export function DeleteEntity(params = {}) {
return request('/trace/code/farmMange/delete', {
method: 'DELETE',
params,
@ -43,7 +43,7 @@ export function DeleteEntity(params) {
/**
* @Title: 导入
*/
export function ImportEntity(data) {
export function ImportEntity(data = {}) {
return request('/trace/code/farmMange/import', {
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' },
@ -54,7 +54,7 @@ export function ImportEntity(data) {
/**
* @Title: 导出
*/
export function ExportEntity(params) {
export function ExportEntity(params = {}) {
return request('/trace/code/farmMange/export', {
method: 'GET',
params,
@ -63,11 +63,10 @@ export function ExportEntity(params) {
}
/**
* @Title: 地址列表
* @Title: 详情
*/
export function GetAreaList(params) {
return request('/trace/code/farmMange/areas', {
export function GetEntity(params = {}) {
return request(`/trace/code/farmMange/qualityCheck/${params?.id}`, {
method: 'GET',
params,
});
}

View File

@ -0,0 +1,41 @@
import request from '@/utils/axios';
/**
* @Title: 列表
*/
export function GetEntityList(params) {
return request('/trace/code/qualityManage/page', {
method: 'GET',
params,
});
}
/**
* @Title: 新增
*/
export function AddEntity(data) {
return request('/trace/code/qualityManage/save', {
method: 'POST',
data,
});
}
/**
* @Title: 修改
*/
export function UpdateEntity(data) {
return request('/trace/code/qualityManage/edit', {
method: 'PUT',
data,
});
}
/**
* @Title: 删除
*/
export function DeleteEntity(params) {
return request('/trace/code/qualityManage/delete', {
method: 'DELETE',
params,
});
}

View File

@ -17,6 +17,7 @@ export const CRUD_OPTIONS = {
align: 'center',
headerAlign: 'center',
gridBtn: false,
columnBtn: false,
addBtn: true,
viewBtn: false,
editBtn: false,
@ -26,4 +27,5 @@ export const CRUD_OPTIONS = {
column: [],
menuWidth: 100,
actions: [],
dialogDrag: true,
};

View File

@ -3,9 +3,10 @@ import { constantRoutes, notFoundRouter } from '@/router';
import { createAsyncRoutes, filterAsyncRoutes, filterKeepAlive } from '@/utils/router';
import { useUserStore } from '@/store/modules/user';
import { getTree } from '@/utils';
import { GenKey } from '@/config';
export const usePermissionStore = defineStore({
id: 'permissionStore',
id: GenKey('permissionStore'),
state: () => ({
// 路由
routes: [],

View File

@ -1,8 +1,9 @@
import { defineStore } from 'pinia';
import { GenKey } from '@/config';
import router from '@/router';
export const useTagsViewStore = defineStore({
id: 'tagsViewStore',
id: GenKey('tagsViewStore'),
state: () => ({
activeTabsValue: '/home',
visitedViews: [],

View File

@ -3,10 +3,10 @@ import { GenKey } from '@/config';
import { isEmpty, encode, decode } from '@/utils';
export const useUserStore = defineStore({
id: GenKey('USER_STATE'),
id: GenKey('userStore'),
state: () => ({
token:
'eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX2tleSI6IjA0ZjNmZTE5LTc5ZWYtNGMxNy1iNWQ4LTE5YjA0MTkyNTZiMyIsInVzZXJuYW1lIjoiYWRtaW4ifQ.nGVLjwzO7K6MO9DbKKhfmwsoinDig5tsGWGEb3jcOtUQHcyJhTTvvuq3zAxNHSFRm9Nly_MSEcRV6oVcz3gT_w',
'eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX2tleSI6IjU0ZGVjMzk4LTRhZmQtNDUyOS1hYjFkLTExZGZlZjU5NjJhOSIsInVzZXJuYW1lIjoiYWRtaW4ifQ._bHoqldw4oL_MqZxrHv81ShD3Z0GJYSdWietR1mHAfKluNi3SSs1PRZNrq1v-WTQyXgz1QgsBimPB48iqP7o6Q',
userInfo: {},
currentOrg: null,
orgList: [],

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2022-02-23 21:12:37
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-02-07 15:21:48
* @LastEditTime: 2025-02-10 14:42:53
*/
import lodash from 'lodash';
import dayjs from 'dayjs';
@ -293,41 +293,55 @@ export const obj2Param = (json) => {
export const getAssetsFile = (url) => {
return new URL(`../assets/${url}`, import.meta.url);
};
/**
* @Title: a链接方式文件下载
* @param {void} content:
* @param {void} fileName:
* @return {void}
*/
export const downloadLink = (url, fileName) => {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = url;
elink.target = '_blank';
elink.click();
elink.remove();
};
/**
* @Title: 下载文件
* @param {void} content:
* @param {void} url:
* @param {void} fileName:
* @param {void} fileType:
* @return {void}
*/
export const downloadFile = (content, fileName) => {
const blob = new Blob([content]);
export const downloadFile = async (url, fileName, fileType) => {
let blob = null;
try {
switch (fileType) {
case 'image': {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = url;
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
blob = await new Promise((resolve) => {
canvas.toBlob(resolve, 'image/jpeg');
});
break;
}
case 'blob': {
blob = new Blob([url]);
break;
}
}
if ('download' in document.createElement('a')) {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
elink.href = blob ? URL.createObjectURL(blob) : url;
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
blob && URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
} else {
navigator.msSaveBlob(blob, fileName);
}
} catch (error) {
console.error('下载出错:', error);
}
};
/**
* @Title 模拟休眠

View File

@ -8,12 +8,12 @@
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@size-change="sizeChange"
@current-change="currentChange"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
@ -24,13 +24,6 @@
<el-button type="success" icon="download" @click="onExport">导出</el-button>
</template>
<template #qualityReportUrl="{ row }">
<el-button v-if="row.qualityReportUrl" type="primary" text @click="downloadLink(row.qualityReportUrl, `${row.productName}-质检报告.pdf`)">
下载
</el-button>
<span v-else>暂无</span>
</template>
<template #productUrl-form="{ column }">
<el-upload class="custom-form__uploader" action="#" :show-file-list="false" accept="image/*" :limit="1" :http-request="rowUploadPicture">
<img v-if="state.form.base64" :src="state.form.base64" class="custom-form__uploader__img" />
@ -38,6 +31,10 @@
</el-upload>
</template>
<template #customInfo-form="{ column }">
<custom-info :row="state.currentRow" />
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
@ -49,22 +46,28 @@
@on-download="onDownloadExcel"
@on-confirm="onUploadExcel"
/>
<custom-quality-add ref="qualityAddRef" :row="state.currentRow" />
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, imageToBase64, getAssetsFile, downloadLink, downloadFile } from '@/utils';
import { isEmpty, imageToBase64, getAssetsFile, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
import { CommonUpload } from '@/apis';
import { GetEntityList, GetAreaList, AddEntity, UpdateEntity, DeleteEntity, ImportEntity, ExportEntity } from '@/apis/coding';
import { compact } from 'lodash';
import { GetEntityList, AddEntity, UpdateEntity, DeleteEntity, ImportEntity, ExportEntity } from '@/apis/coding';
import CustomInfo from './info.vue';
import CustomQualityAdd from '../quality/form.vue';
const { VITE_APP_DICDATA_API, VITE_APP_BASE_API } = import.meta.env;
const { VITE_APP_SYSTEM_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const crudRef = ref(null);
const importExcelRef = ref(null);
const qualityAddRef = ref(null);
const state = reactive({
loading: false,
query: {
@ -89,11 +92,6 @@ const state = reactive({
prop: 'harvestBatch',
width: 200,
display: false,
// rules: {
// required: true,
// message: '',
// trigger: 'blur',
// },
},
{
label: '产品名称',
@ -115,7 +113,7 @@ const state = reactive({
label: 'dictLabel',
value: 'dictValue',
},
dicUrl: `${VITE_APP_DICDATA_API}/system/dict/data/list?pageNum=1&pageSize=20&dictType=sys_product_type`,
dicUrl: `${VITE_APP_SYSTEM_API}/system/dict/data/list?pageNum=1&pageSize=20&dictType=sys_product_type`,
dicHeaders: {
authorization: UserStore.token,
},
@ -144,7 +142,7 @@ const state = reactive({
label: 'dictLabel',
value: 'dictValue',
},
dicUrl: `${VITE_APP_DICDATA_API}/system/dict/data/list?pageNum=1&pageSize=20&dictType=sys_unit_type`,
dicUrl: `${VITE_APP_SYSTEM_API}/system/dict/data/list?pageNum=1&pageSize=20&dictType=sys_unit_type`,
dicHeaders: {
authorization: UserStore.token,
},
@ -168,17 +166,17 @@ const state = reactive({
return row.qualityGuaranteePeriod + '天';
},
},
{
label: '质检结果',
prop: 'qualityResult',
display: false,
},
{
label: '质检报告',
prop: 'qualityReportUrl',
display: false,
slot: true,
},
// {
// label: '',
// prop: 'qualityResult',
// display: false,
// },
// {
// label: '',
// prop: 'qualityReportUrl',
// display: false,
// slot: true,
// },
{
label: '采收日期',
prop: 'datetime',
@ -235,27 +233,27 @@ const state = reactive({
{
label: '原产地',
prop: 'originAddress',
type: 'cascader',
search: true,
display: false,
overHidden: true,
width: 200,
},
{
label: '原产地',
prop: 'cities',
type: 'cascader',
// search: true,
hide: true,
props: {
// expandTrigger: 'click',
label: 'areaName',
value: 'areaCode',
children: 'areaChildVOS',
lazy: true,
lazyLoad: async (node, resolve) => {
console.log(361, node);
const { data } = await GetAreaList({ areaCode: node.value });
resolve(data ?? []);
},
},
dicUrl: `${VITE_APP_BASE_API}/trace/code/farmMange/areas`,
dicUrl: `${VITE_APP_SYSTEM_API}/system/area/region?areaCode=530000`,
dicHeaders: {
authorization: UserStore.token,
},
dicFormatter: (res) => res?.data ?? [],
dicFormatter: (res) => res.data ?? [],
// change: (o) => setCityChange(o),
rules: {
required: true,
message: '请选择',
@ -311,6 +309,15 @@ const state = reactive({
width: 200,
display: false,
},
{
label: '',
labelWidth: 0,
prop: 'customInfo',
addDisplay: false,
editDisplay: false,
viewDisplay: true,
span: 24,
},
],
actions: [
{
@ -329,6 +336,11 @@ const state = reactive({
icon: 'delete',
event: ({ row }) => rowDel(row),
},
{
name: '新增质检',
icon: 'plus',
event: ({ row }) => rowAdd(row),
},
],
},
pageData: {
@ -337,8 +349,10 @@ const state = reactive({
pageSize: 10,
},
data: [],
currentRow: {},
});
//
const loadData = async () => {
state.loading = true;
GetEntityList(state.query)
@ -355,14 +369,13 @@ const loadData = async () => {
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
});
};
// GetAreaList();
loadData();
//
@ -403,6 +416,7 @@ const selectionChange = (rows) => {
//
const rowView = (row) => {
row.base64 = row.productUrl;
state.currentRow = row;
crudRef.value.rowView(row);
};
@ -419,9 +433,33 @@ const rowUploadPicture = async ({ file }) => {
}
};
const setCity = (row) => {
if (!isEmpty(row.cities)) {
row.province = row?.cities[0] ?? null;
row.city = row?.cities[1] ?? null;
row.county = row?.cities[2] ?? null;
row.village = row?.cities[3] ?? null;
}
};
// const setCityChange = ({ value, dic }) => {
// debugger;
// const labels = [];
// let currentOptions = dic;
// value.forEach((val) => {
// const option = dic.find((item) => item.areaCode === val);
// if (option) {
// labels.push(option.label);
// currentOptions = option.children || [];
// }
// });
// state.form.originAddress = labels.join(' / ');
// };
//
const rowSave = (row, done, loading) => {
delete row.base64;
setCity(row);
AddEntity(row)
.then((res) => {
if (res.code === 200) {
@ -441,10 +479,12 @@ const rowSave = (row, done, loading) => {
//
const rowEdit = (row) => {
row.base64 = row.productUrl;
row.cities = compact([row.province, row.city, row.county ?? '', row.village ?? '']);
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
delete row.base64;
setCity(row);
UpdateEntity(row)
.then((res) => {
if (res.code === 200) {
@ -490,6 +530,12 @@ const rowDel = (row, index, done) => {
onDel([row]);
};
//
const rowAdd = (row) => {
state.currentRow = row;
qualityAddRef?.value && qualityAddRef.value.show();
};
//
const onUpload = () => {
importExcelRef?.value && importExcelRef.value.show();
@ -512,7 +558,7 @@ const onUploadExcel = (formData) => {
};
const onDownloadExcel = (url) => {
downloadLink(url, `采收赋码-导入模板.xlsx`);
downloadFile(url, `采收赋码-导入模板.xlsx`, 'blob');
};
//
@ -526,7 +572,7 @@ const onExport = () => {
ExportEntity(state.query)
.then((res) => {
if (res.status === 200) {
downloadFile(res.data, `${fileName}.xlsx`);
downloadFile(res.data, `${fileName}.xlsx`, 'blob');
app.$message.success('导出成功!');
}
})

View File

@ -0,0 +1,175 @@
<template>
<div class="custom-info">
<avue-crud ref="crudRef" :table-loading="state.loading" :data="state.list" :option="state.options">
<template #header>
<h5 class="custom-form__title">质检记录</h5>
</template>
<template #qualityReportUrl="{ row: item }">
<el-button
v-if="item.qualityReportUrl"
type="primary"
text
@click="downloadFile(item.qualityReportUrl, `${item.productName}-质检报告.png`, 'image')"
>
下载
</el-button>
<span v-else>暂无</span>
</template>
</avue-crud>
</div>
</template>
<script setup>
import { reactive, ref, watch } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { downloadFile } from '@/utils';
import { GetEntity } from '@/apis/coding';
const props = defineProps({
row: {
type: Object,
default: () => {},
},
});
const app = useApp();
const crudRef = ref(null);
const state = reactive({
loading: false,
// query: {
// current: 1,
// size: 10,
// },
options: {
...CRUD_OPTIONS,
index: false,
addBtn: false,
refreshBtn: false,
selection: false,
menu: false,
column: [
{
label: '溯源码',
prop: 'id',
width: 200,
fixed: true,
},
{
label: '采收批次',
prop: 'harvestBatch',
width: 200,
},
{
label: '产品名称',
prop: 'productName',
width: 200,
},
{
label: '质检结果',
prop: 'qualityResultType',
type: 'select',
dicData: [
{
label: '合格',
value: 0,
},
{
label: '不合格',
value: 1,
},
],
},
{
label: '质检类型',
prop: 'qualityType',
type: 'select',
dicData: [
{
label: '检测机构',
value: 0,
},
{
label: '自检',
value: 1,
},
],
},
{
label: '质检机构',
prop: 'department',
width: 200,
overHidden: true,
},
{
label: '质检项目',
prop: 'qualityProject',
},
{
label: '质检报告',
prop: 'qualityReportUrl',
slot: true,
},
{
label: '检测说明',
prop: 'qualityDescribe',
type: 'textarea',
overHidden: true,
resize: 'none',
width: 200,
},
{
label: '质检人',
prop: 'qualityPerson',
},
{
label: '质检时间',
prop: 'qualityTime',
width: 200,
},
],
},
// pageData: {
// total: 0,
// currentPage: 1,
// pageSize: 10,
// },
data: {},
list: [],
});
//
const loadData = () => {
state.loading = true;
GetEntity({ id: props.row.id })
.then((res) => {
if (res.code === 200) {
state.data = res.data;
state.list = res.data?.productInfo ?? [];
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
state.loading = false;
});
};
watch(
() => props.row,
(val) => {
val?.id && loadData();
},
{
deep: true,
immediate: true,
}
);
</script>
<style lang="scss" scoped>
.custom-info {
:deep(.avue-crud__header) {
display: none !important;
}
}
</style>

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2022-09-18 21:24:29
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-02-07 10:01:57
* @LastEditTime: 2025-02-08 17:26:25
*/
import { defineConfig, loadEnv } from 'vite';
@ -23,7 +23,6 @@ import { resolve } from 'path';
const useDevMode = true;
export default defineConfig(({ command, mode }) => {
console.log('vite.config.js', command, mode, loadEnv(mode, process.cwd()));
const {
VITE_PORT,
VITE_APP_NAME,
@ -31,8 +30,8 @@ export default defineConfig(({ command, mode }) => {
VITE_APP_BASE_URL,
VITE_APP_UPLOAD_API,
VITE_APP_UPLOAD_URL,
VITE_APP_DICDATA_API,
VITE_APP_DICDATA_URL,
VITE_APP_SYSTEM_API,
VITE_APP_SYSTEM_URL,
} = loadEnv(mode, process.cwd());
const config = {
base: './',
@ -53,17 +52,17 @@ export default defineConfig(({ command, mode }) => {
[VITE_APP_BASE_API]: {
target: VITE_APP_BASE_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/traceApis/, ''),
rewrite: (path) => path.replace(/^\/apis/, ''),
},
[VITE_APP_UPLOAD_API]: {
target: VITE_APP_UPLOAD_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/uploadApis/, ''),
},
[VITE_APP_DICDATA_API]: {
target: VITE_APP_DICDATA_URL,
[VITE_APP_SYSTEM_API]: {
target: VITE_APP_SYSTEM_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/dicDataApis/, ''),
rewrite: (path) => path.replace(/^\/systemApis/, ''),
},
},
},

View File

@ -3,10 +3,10 @@
* @Author: zenghua.wang
* @Date: 2022-02-23 21:12:37
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-01-25 17:03:51
* @LastEditTime: 2025-02-10 14:45:39
*/
import lodash from 'lodash';
import moment from 'moment';
import dayjs from 'dayjs';
import { Base64 } from 'js-base64';
/**
@ -90,7 +90,7 @@ export const setDefaultOption = (options, prop, defaultVal) => {
return options[prop] === undefined ? defaultVal : options.prop;
};
/**
* 设置字典值
* @Title 设置字典值
* @param {*} columns
* @param {*} key
* @param {*} data
@ -107,7 +107,7 @@ export const setDicData = (columns, key, data = []) => {
}
};
/**
* 求字段lable
* @Title 求字段lable
* @param {*} tree
* @returns
*/
@ -124,7 +124,35 @@ export const setDicLabel = (dicData, value) => {
return label;
};
/**
* 加密
* @Title 数组交集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const intersectionArray = (arr1 = [], arr2 = []) => {
return arr1.filter((item) => arr2.includes(item));
};
/**
* @Title 数组并集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const unionArray = (arr1 = [], arr2 = []) => {
return Array.from(new Set([...arr1, ...arr2]));
};
/**
* @Title 数组差集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const differenceArray = (arr1 = [], arr2 = []) => {
const s = new Set(arr2);
return arr1.filter((x) => !s.has(x));
};
/**
* @Title 加密
* @param {*} n
* @returns
*/
@ -146,7 +174,7 @@ export const encode = (n, flag = false) => {
return n;
};
/**
* 解密
* @Title 解密
* @param {*} e
* @returns
*/
@ -190,6 +218,22 @@ export const imageToBase64 = (file) => {
export const bufferToBase64 = (buffer) => {
return 'data:image/jpeg;base64,' + window.btoa(new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), ''));
};
/**
* @Title blob转json
* @param {*} file
* @returns
*/
export const blobToJSON = (blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(blob, 'utf-8');
reader.onload = () => {
const res = !isEmpty(reader.result) ? JSON.parse(reader.result) : reader.result;
resolve(res);
};
reader.onerror = reject;
});
};
/**
* @Title 将array转化为树
* @param tree
@ -227,43 +271,77 @@ export const getUrlQuery = (name) => {
const usp = new URLSearchParams(search);
return usp.get(name);
};
/**
* @Title 将Object参数转换为字符串
* @param {*} json
* @returns
*/
export const obj2Param = (json) => {
if (!json) return '';
return Object.keys(json)
.map((key) => {
if (isEmpty(json[key])) return '';
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
})
.join('&');
};
/**
* @Title 获取静态资源文件
* @param {*} url
* @returns
*/
export const getAssetsFile = (url) => {
return new URL(`../assets/images/${url}`, import.meta.url);
return new URL(`../assets/${url}`, import.meta.url);
};
/**
* @Title 替换图片url字段值
* @param {*} url
* @returns
*/
export const setUploadField = (url) => {
if (isEmpty(url) || url.includes('http')) return null;
return url;
};
/**
* @Title: a链接方式文件下载
* @param {void} content:
* @Title: 下载文件
* @param {void} url:
* @param {void} fileName:
* @param {void} fileType:
* @return {void}
*/
export const dowloadLink = (content, fileName) => {
const blob = new Blob([content]);
export const downloadFile = async (url, fileName, fileType) => {
let blob = null;
try {
switch (fileType) {
case 'image': {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = url;
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
});
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
blob = await new Promise((resolve) => {
canvas.toBlob(resolve, 'image/jpeg');
});
break;
}
case 'blob': {
blob = new Blob([url]);
break;
}
}
if ('download' in document.createElement('a')) {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
elink.href = blob ? URL.createObjectURL(blob) : url;
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
blob && URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
} else {
navigator.msSaveBlob(blob, fileName);
}
} catch (error) {
console.error('下载出错:', error);
}
};
/**
* @Title 模拟休眠
@ -275,20 +353,106 @@ export const sleep = (duration = 0) =>
setTimeout(resolve, duration);
});
/**
* 日期格式化
* @Title 创建id
* @param {*} prefix
* @returns
*/
export const createId = (prefix) => {
const val = Date.now() + Math.ceil(Math.random() * 99999);
return isEmpty(prefix) ? val : prefix + '-' + val;
};
/**
* @Title 生成数据
* @param {*} duration
* @returns
*/
export const mockData = (item = {}, len = 1) => {
const list = [];
for (let i = 0; i < len; i++) {
let temp = { ...item, id: createId() };
list.push(temp);
}
return list;
};
/**
* @Title 日期格式化
* @param {*} date
* @param {*} format
* @returns
*/
export const dateFormat = (datetime, type = 'yyyy-MM-dd') => {
return moment(datetime).format(type);
export const dateFormat = (datetime, formater = 'YYYY-MM-DD hh:mm:ss') => {
if (datetime instanceof Date || datetime) {
return dayjs(datetime).format(formater);
} else {
return null;
}
};
/**
* 上日///
* @Title 字符串转日期
* @param {*} str
* @returns
*/
export const lastDate = (last = 0, date = 'month', type = 'yyyy-MM-dd') => {
if (date === 'day') {
return moment().subtract(last, 'day').endOf('day').format(type);
}
return moment().subtract(last, date).format(type);
export const toDate = (str) => {
return !isEmpty(str) ? dayjs(str) : dayjs();
};
/**
* @Title 字符串转日期
* @param {*} str
* @returns
*/
export const getDate = (num, type, formater = 'YYYY-MM-DD', start = true) => {
const date = dayjs().subtract(num, type);
return start ? date.startOf(type).format(formater) : date.endOf(type).format(formater);
};
/**
* @Title: 获取时间差
* @param start
* @param end
* @param type
* @returns
*/
export const getDiffTime = (start, end, type) => {
const startTime = dayjs(start);
const endTime = dayjs(end);
const duration = endTime.diff(startTime);
let diff = 0;
switch (type) {
case 'DD': {
diff = duration / (1000 * 60 * 60 * 24);
break;
}
case 'HH': {
diff = duration / (1000 * 60 * 60);
break;
}
case 'mm': {
diff = duration / (1000 * 60);
break;
}
}
return Math.round(diff);
};
/**
* @Title: 开始日期
* @param last
* @param type
* @param formater
* @returns
*/
export const startDate = (num, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss') => {
if (num === 'now') return dayjs().format(formater);
if (typeof num === 'string') return dayjs(num).startOf(type).format(formater);
return num === 0 ? dayjs().startOf(type).format(formater) : dayjs().subtract(num, type).startOf(type).format(formater);
};
/**
* @Title: 结束日期
* @param num
* @param type
* @param formater
* @returns
*/
export const endDate = (num = 0, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss') => {
if (num === 'now') return dayjs().format(formater);
if (typeof num === 'string') return dayjs(num).endOf(type).format(formater);
return num === 0 ? dayjs().endOf(type).format(formater) : dayjs().subtract(num, type).endOf(type).format(formater);
};

View File

@ -23,7 +23,6 @@ import { resolve } from 'path';
const useDevMode = true;
export default defineConfig(({ command, mode }) => {
console.log('vite.config.js', command, mode, loadEnv(mode, process.cwd()));
const { VITE_PORT, VITE_APP_NAME, VITE_APP_BASE_API, VITE_APP_BASE_URL, VITE_APP_UPLOAD_API, VITE_APP_UPLOAD_URL } = loadEnv(mode, process.cwd());
const config = {
base: './',