This commit is contained in:
Xulinchuan 2025-06-13 15:26:06 +08:00
commit cec1b42f99
23 changed files with 510 additions and 675 deletions

View File

@ -16,6 +16,6 @@ VITE_APP_UPLOAD_API = '/uploadApis'
# VITE_APP_BASE_URL = 'http://47.109.205.240:8080' # VITE_APP_BASE_URL = 'http://47.109.205.240:8080'
# VITE_APP_UPLOAD_URL = 'http://47.109.205.240:9300' # VITE_APP_UPLOAD_URL = 'http://47.109.205.240:9300'
# 内网接口地址 # 内网接口地址
VITE_APP_BASE_URL = 'http://192.168.18.74:8080' VITE_APP_BASE_URL = 'http://192.168.18.99:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080' VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080'
# VITE_APP_VIST_URL = 'http://192.168.18.99' # VITE_APP_VIST_URL = 'http://192.168.18.99'

View File

@ -13,5 +13,5 @@ VITE_APP_UPLOAD_API = '/uploadApis'
# VITE_APP_UPLOAD_URL = 'http://47.109.205.240:9300' # VITE_APP_UPLOAD_URL = 'http://47.109.205.240:9300'
# 内网接口地址 # 内网接口地址
VITE_APP_BASE_URL = 'http://192.168.18.74:8080' VITE_APP_BASE_URL = 'http://192.168.18.99:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080' VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080'

View File

@ -18,6 +18,13 @@ export function getLandList(params) {
params, params,
}); });
} }
// 新增全部土地信息POST
export function createLand(data = {}) {
return request('/land-resource/landManage/v1/save', {
method: 'POST',
data,
});
}
/** /**
* 保存土地基础信息 * 保存土地基础信息

View File

@ -120,6 +120,8 @@ const containerStyle = computed(() => ({
text-align: right; text-align: right;
line-height: 32px; line-height: 32px;
width: 120px; width: 120px;
box-sizing: border-box;
padding-right: 12px;
} }
.controls { .controls {
display: flex; display: flex;

View File

@ -0,0 +1,15 @@
import { defineStore } from 'pinia';
import { getEnterList } from '@/apis/businessEntity';
export const useCoop = defineStore('useCoop', {
state: () => ({
data: {},
}),
actions: {
//订单
getData(params) {
return Promise.resolve((useCoop().$state.data = params));
},
},
getters: {},
});

View File

@ -30,154 +30,96 @@
</avue-crud> </avue-crud>
<!-- 新增弹窗 --> <!-- 新增弹窗 -->
<el-dialog v-model="addDialogVisible" title="新增" width="800px" top="8vh"> <el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-tabs v-model="activeTab"> <el-tabs v-model="activeTab" class="tabs-wrapper">
<el-tab-pane label="土地基本信息" name="basic"> <el-tab-pane label="土地基本信息" name="basic">
<el-form ref="basicFormRef" :model="formDataBasic" :rules="basicRules" label-width="120px" class="form-container"> <p class="form-title">编辑基本信息</p>
<el-form-item label="地块名称" prop="landName"> <el-form ref="basicFormRef" :model="formDataBasic" :disabled="isReadonly" label-width="120px">
<el-input v-model="formDataBasic.landName" placeholder="请输入地块名称" /> <el-row :gutter="20">
</el-form-item> <el-col :span="12">
<el-form-item label="面积(亩)" prop="area"> <el-form-item label="地块名称" prop="landName">
<el-input-number v-model="formDataBasic.area" :min="0" :precision="2" :step="0.1" controls-position="right" style="width: 100%" /> <el-input v-model="formDataBasic.landName" placeholder="请输入地块名称" />
</el-form-item> </el-form-item>
<el-form-item label="土地类型" prop="landType"> <el-form-item label="土地类型" prop="landType">
<el-select v-model="formDataBasic.landType" placeholder="选择土地类型" clearable> <el-select v-model="formDataBasic.landType" placeholder="选择土地类型" clearable>
<el-option v-for="item in landTypeOptions" :key="item.id" :label="item.landType" :value="item.id" /> <el-option v-for="item in landTypeOptions" :key="item.id" :label="item.landType" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="具体位置" prop="address"> <el-form-item label="具体位置" prop="address">
<el-input v-model="formDataBasic.address" placeholder="请输入具体位置" /> <el-input v-model="formDataBasic.address" placeholder="请输入具体位置" />
</el-form-item> </el-form-item>
<el-form-item label="所属网格" prop="gridId"> <el-form-item label="土壤类型" prop="soilTypeId">
<AreaCascader v-model:region-code="formDataBasic.regionCode" v-model:grid-id="formDataBasic.gridId" label="" :width="500" /> <url-select
</el-form-item> v-model="formDataBasic.soilTypeId"
<el-form-item label="土壤类型" prop="soilTypeId"> placeholder="选择土壤类型"
<url-select url="/land-resource/baseInfo/soilTypePage"
v-model="formDataBasic.soilTypeId" :params="{ current: 1, size: 100 }"
placeholder="选择土壤类型" label-key="soilType"
url="/land-resource/baseInfo/soilTypePage" value-key="id"
:params="{ current: 1, size: 100 }" :clearable="true"
label-key="soilType" />
value-key="id" </el-form-item>
:clearable="true" <el-form-item label="土地照片" prop="landUrl">
/> <FileUploader v-model="formDataBasic.landUrl" :limit="1" />
</el-form-item> </el-form-item>
<el-form-item label="土地照片" prop="landUrl"> </el-col>
<el-upload <el-col :span="12">
:http-request="customUploadRequest" <el-form-item label="面积(亩)" prop="area">
:on-success="(res, file) => handleUploadSuccess(res, file, 'basic')" <el-input-number v-model="formDataBasic.area" :min="0" :precision="2" :step="0.1" controls-position="right" style="width: 100%" />
multiple </el-form-item>
:show-file-list="true"
> <el-form-item label="" label-width="0" prop="gridId">
<template #trigger> <AreaCascader v-model:region-code="formDataBasic.regionCode" v-model:grid-id="formDataBasic.gridId" label="" :split-rows="true" />
<el-button type="primary">点击上传</el-button> </el-form-item>
</template>
</el-upload> <el-form-item label="土地范围" prop="scope">
</el-form-item> <el-input v-model="formDataBasic.scope" placeholder="请输入土地范围" />
<el-form-item label="土地范围" prop="scope"> <!-- <Attrs v-model:attrs="formDataBasic.scope" type="add" accept="image/*" /> -->
<el-input v-model="formDataBasic.scope" placeholder="请输入土地范围" /> </el-form-item>
<!-- <Attrs v-model:attrs="formDataBasic.scope" type="add" accept="image/*" /> --> </el-col>
</el-form-item> </el-row>
</el-form> </el-form>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="土地产权信息" name="property"> <el-tab-pane label="土地产权信息" name="property">
<el-form ref="propertyFormRef" :model="formDataProperty" :rules="propertyRules" label-width="120px" class="form-container"> <p class="form-title">编辑产权信息</p>
<el-form-item label="地块名称"> <el-form ref="propertyFormRef" style="width: 60%" :model="formDataProperty" :disabled="isReadonly" label-width="120px">
<el-input v-model="formDataProperty.landName" disabled /> <el-row :gutter="20">
</el-form-item> <el-form-item label="地块名称">
<el-form-item label="产权人姓名" prop="propertyName"> <el-input v-model="formDataProperty.landName" disabled />
<el-input v-model="formDataProperty.propertyName" placeholder="请输入产权人姓名" /> </el-form-item>
</el-form-item> <el-form-item label="产权人姓名" prop="propertyName">
<el-form-item label="联系方式" prop="propertyPhone"> <el-input v-model="formDataProperty.propertyName" placeholder="请输入产权人姓名" />
<el-input v-model="formDataProperty.propertyPhone" placeholder="请输入产权人联系方式" /> </el-form-item>
</el-form-item> <el-form-item label="联系方式" prop="propertyPhone">
<el-form-item label="产权编号" prop="landCode"> <el-input v-model="formDataProperty.propertyPhone" placeholder="请输入产权人联系方式" />
<el-input v-model="formDataProperty.landCode" placeholder="请输入产权编号" /> </el-form-item>
</el-form-item> <el-form-item label="产权编号" prop="landCode">
<el-form-item label="产权证书" prop="propertyCertificateUrl"> <el-input v-model="formDataProperty.landCode" placeholder="请输入产权编号" />
<FileUploader v-model="formDataProperty.propertyCertificateUrl" :limit="1" /> </el-form-item>
</el-form-item> <el-form-item label="产权证书" prop="propertyCertificateUrl">
<FileUploader v-model="formDataProperty.propertyCertificateUrl" :limit="1" />
</el-form-item>
</el-row>
</el-form> </el-form>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="addDialogVisible = false">取消</el-button> <el-button @click="visible = false">取消</el-button>
<el-button v-if="activeTab === 'basic'" type="primary" @click="handleAddSubmit('basic')">提交</el-button>
<el-button v-if="activeTab === 'basic'" :disabled="disabledProperty" @click="activeTab = 'property'">下一步</el-button>
<el-button v-else @click="activeTab = 'basic'">上一步</el-button>
<el-button v-if="activeTab === 'property'" type="primary" @click="handleAddSubmit('property')"> 提交 </el-button>
</span>
</template>
</el-dialog>
<!-- 查看和编辑共用弹窗 --> <!-- 土地基本信息 tab -->
<el-dialog v-model="viewEditDialogVisible" :title="viewEditTitle" width="800px" top="8vh"> <template v-if="activeTab === 'basic'">
<el-form <el-button type="primary" @click="submitBasicInfo">暂存</el-button>
ref="viewEditFormRef" <el-button @click="activeTab = 'property'">下一步</el-button>
:model="viewEditFormData" </template>
:rules="isView ? {} : editRules"
label-width="120px"
class="view-edit-form"
:disabled="isView"
>
<h3 class="section-title">土地基本信息</h3>
<el-form-item label="地块名称" prop="landName">
<el-input v-model="viewEditFormData.landName" />
</el-form-item>
<el-form-item label="面积(亩)" prop="area">
<el-input-number v-model="viewEditFormData.area" :min="0" :precision="2" :step="0.1" controls-position="right" style="width: 100%" />
</el-form-item>
<el-form-item label="土地类型" prop="landType">
<el-select v-model="viewEditFormData.landType" clearable>
<el-option v-for="item in landTypeOptions" :key="item.id" :label="item.landType" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="具体位置" prop="address">
<el-input v-model="viewEditFormData.address" />
</el-form-item>
<el-form-item label="所属网格" prop="gridId">
<AreaCascader v-model="viewEditFormData.regionCode" v-model:grid-id="viewEditFormData.gridId" :width="500" label="" />
</el-form-item>
<el-form-item label="土壤类型" prop="soilTypeId">
<url-select
v-model="viewEditFormData.soilTypeId"
url="/land-resource/baseInfo/soilTypePage"
:params="{ current: 1, size: 100 }"
label-key="soilType"
value-key="id"
:clearable="true"
/>
</el-form-item>
<el-form-item label="土地照片" prop="landUrl">
<FileUploader v-model="viewEditFormData.landUrl" :limit="1" />
</el-form-item>
<el-form-item label="土地范围" prop="scope">
<el-input v-model="viewEditFormData.scope" />
</el-form-item>
<h3 class="section-title">土地产权信息</h3> <!-- 土地产权信息 tab -->
<el-form-item label="产权人姓名" prop="propertyName"> <template v-else>
<el-input v-model="viewEditFormData.propertyName" /> <el-button @click="activeTab = 'basic'">上一步</el-button>
</el-form-item> <el-button v-if="formDataProperty.landId" type="primary" @click="submitPropertyInfo">提交</el-button>
<el-form-item label="联系方式" prop="propertyPhone"> <el-button v-else type="primary" @click="submitAll">保存</el-button>
<el-input v-model="viewEditFormData.propertyPhone" /> </template>
</el-form-item>
<el-form-item label="产权编号" prop="landCode">
<el-input v-model="viewEditFormData.landCode" />
</el-form-item>
<el-form-item label="产权证书" prop="propertyCertificateUrl">
<FileUploader v-model="viewEditFormData.propertyCertificateUrl" :limit="1" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="viewEditDialogVisible = false">{{ isView ? '关闭' : '取消' }}</el-button>
<el-button v-if="isView" type="primary" @click="handleEdit">编辑</el-button>
<el-button v-if="!isView" type="primary" @click="handleView">查看</el-button>
<el-button v-if="!isView" type="primary" @click="handleViewEditSubmit"> 提交 </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
@ -187,8 +129,9 @@
<script setup> <script setup>
import { ref, reactive, onMounted, computed, nextTick } from 'vue'; import { ref, reactive, onMounted, computed, nextTick } from 'vue';
import { CRUD_OPTIONS } from '@/config'; import { CRUD_OPTIONS } from '@/config';
import { getLandList, saveBaseInfo, saveProperty, editLand, deleteLand } from '@/apis/landResourceManagement/landManagement'; import { getLandList, createLand, saveBaseInfo, saveProperty, editLand, deleteLand } from '@/apis/landResourceManagement/landManagement';
import { CommonUpload } from '@/apis/index'; import { CommonUpload } from '@/apis/index';
import { ElMessage, ElMessageBox } from 'element-plus';
import request from '@/utils/axios'; import request from '@/utils/axios';
// ============================== // ==============================
@ -196,17 +139,14 @@ import request from '@/utils/axios';
// ============================== // ==============================
const loading = ref(false); const loading = ref(false);
const crudRef = ref(); const crudRef = ref();
const addDialogVisible = ref(false); const visible = ref(false);
const isReadonly = ref(false);
const activeTab = ref('basic'); const activeTab = ref('basic');
const viewEditDialogVisible = ref(false); const dialogTitle = ref('新增');
const isView = ref(false);
//
const isValidationEnabled = ref(false);
// //
const basicFormRef = ref(null); const basicFormRef = ref(null);
const propertyFormRef = ref(null); const propertyFormRef = ref(null);
const viewEditFormRef = ref(null);
// //
const pageData = ref({ const pageData = ref({
@ -251,97 +191,6 @@ const formDataProperty = ref({
propertyCertificateUrl: '', propertyCertificateUrl: '',
}); });
const viewEditFormData = ref({
id: '',
landName: '',
gridId: '',
gridName: '',
area: 0,
landType: '',
landTypeId: '',
landTypeName: '',
address: '',
detailAddress: '',
fullLandType: '',
fullRegionName: '',
soilTypeId: '',
soilType: '',
landUrl: '',
scope: '',
propertyName: '',
propertyPhone: '',
landCode: '',
propertyCertificateUrl: '',
updateTime: '',
});
// OSS URL
const ossUrl = 'http://gov-cloud.oss-cn-chengdu.aliyuncs.com/';
// ==============================
//
// ==============================
const viewEditTitle = computed(() => {
return isView.value ? '查看' : '编辑';
});
const disabledProperty = computed(() => {
return formDataBasic.value.id ? false : true;
});
// ==============================
//
// ==============================
const basicRules = reactive(
isValidationEnabled.value
? {
landName: [{ required: true, message: '请输入地块名称', trigger: 'blur' }],
area: [
{ required: true, message: '请输入面积', trigger: 'blur' },
{
type: 'number',
min: 0,
message: '面积必须大于0',
trigger: 'blur',
},
],
landType: [{ required: true, message: '请选择土地类型', trigger: 'change' }],
address: [{ required: true, message: '请输入具体位置', trigger: 'blur' }],
gridId: [{ required: true, message: '请选择所属网格', trigger: 'change' }],
soilTypeId: [{ required: true, message: '请选择土壤类型', trigger: 'change' }],
landUrl: [{ required: true, message: '请上传土地照片', trigger: 'change' }],
scope: [{ required: true, message: '请输入土地范围', trigger: 'blur' }],
}
: {}
);
const propertyRules = reactive(
isValidationEnabled.value
? {
propertyName: [{ required: true, message: '请输入产权人姓名', trigger: 'blur' }],
propertyPhone: [
{ required: true, message: '请输入联系方式', trigger: 'blur' },
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号码',
trigger: 'blur',
},
],
landCode: [{ required: true, message: '请输入产权编号', trigger: 'blur' }],
propertyCertificateUrl: [{ required: true, message: '请上传产权证书', trigger: 'change' }],
}
: {}
);
const editRules = reactive(
isValidationEnabled.value
? {
...basicRules,
...propertyRules,
}
: {}
);
// ============================== // ==============================
// CRUD // CRUD
// ============================== // ==============================
@ -353,7 +202,7 @@ const option = reactive({
column: [ column: [
{ label: '地块名称', prop: 'landName' }, { label: '地块名称', prop: 'landName' },
{ label: '所属网格', prop: 'gridName' }, { label: '所属网格', prop: 'gridName' },
{ label: '面积', prop: 'area', formatter: (row, column, cellValue) => `${cellValue}` }, { label: '面积', prop: 'area', formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)}` },
{ label: '土地类型', prop: 'landTypeName' }, { label: '土地类型', prop: 'landTypeName' },
{ label: '所属行政区域', prop: 'fullRegionName' }, { label: '所属行政区域', prop: 'fullRegionName' },
{ label: '具体位置', prop: 'address' }, { label: '具体位置', prop: 'address' },
@ -364,16 +213,16 @@ const option = reactive({
{ label: '信息更新时间', prop: 'updateTime' }, { label: '信息更新时间', prop: 'updateTime' },
], ],
actions: [ actions: [
{ // {
name: '查看', // name: '',
icon: 'view', // icon: 'view',
event: ({ row }) => handleView(row), // event: ({ row }) => handleView(row),
}, // },
{ // {
name: '编辑', // name: '',
icon: 'edit', // icon: 'edit',
event: ({ row }) => handleEdit(row), // event: ({ row }) => handleEdit(row),
}, // },
{ {
type: 'danger', type: 'danger',
name: '删除', name: '删除',
@ -440,7 +289,7 @@ const handleAdd = () => {
propertyCertificateUrl: '', propertyCertificateUrl: '',
}; };
addDialogVisible.value = true; visible.value = true;
activeTab.value = 'basic'; activeTab.value = 'basic';
// //
@ -455,22 +304,15 @@ const handleAdd = () => {
}; };
const handleView = (row) => { const handleView = (row) => {
isView.value = true; isReadonly.value = true;
viewEditFormData.value = { ...row }; dialogTitle.value = '土地资源详情';
viewEditDialogVisible.value = true; visible.value = true;
}; };
const handleEdit = (row) => { const handleEdit = (row) => {
isView.value = false; dialogTitle.value = '编辑';
viewEditFormData.value = { ...row }; isReadonly.value = false;
viewEditDialogVisible.value = true; visible.value = true;
//
// nextTick(() => {
// if (viewEditFormRef.value) {
// viewEditFormRef.value.resetFields();
// }
// });
}; };
const handleDelete = (id) => { const handleDelete = (id) => {
@ -500,94 +342,57 @@ const resetSearch = () => {
// ============================== // ==============================
// - // -
// ============================== // ==============================
const handleAddSubmit = async (tab) => { const submitBasicInfo = async () => {
try { try {
if (tab === 'basic') { await basicFormRef.value.validate();
await basicFormRef.value.validate(); const res = await saveBaseInfo(formDataBasic.value);
const res = await saveBaseInfo(formDataBasic.value); if (res.code === 200) {
if (res.code === 200) { formDataProperty.value.id = res.data.id;
activeTab.value = 'property'; formDataProperty.value.landName = formDataBasic.value.landName;
formDataProperty.value.id = res.data.id; ElMessage.success('基本信息已暂存');
formDataProperty.value.landName = formDataBasic.value.landName;
}
} else if (tab === 'property') {
await propertyFormRef.value.validate();
const res = await saveProperty(formDataProperty.value);
if (res.code === 200) {
addDialogVisible.value = false;
getData();
}
} }
} catch (error) { } catch (error) {
console.error('表单提交失败', error); console.error('暂存失败', error);
} }
}; };
const handleViewEditSubmit = async () => { const submitPropertyInfo = async () => {
try { try {
await viewEditFormRef.value.validate(); await propertyFormRef.value.validate();
// if (!formDataProperty.value.id) {
const { id, ...formData } = viewEditFormData.value; ElMessage.error('请先保存基本信息');
if (id) { return;
// }
// await updateLandInfo({ id, ...formData }); const res = await saveProperty(formDataProperty.value);
await editLand({ id, ...formData }); if (res.code === 200) {
viewEditDialogVisible.value = false; ElMessage.success('产权信息提交成功');
visible.value = false;
getData(); getData();
} }
} catch (error) { } catch (error) {
console.error('表单提交失败', error); console.error('产权信息提交失败', error);
} }
}; };
// ============================== const submitAll = async () => {
// -
// ==============================
const customUploadRequest = async (options) => {
const formData = new FormData();
formData.append('file', options.file);
try { try {
const response = await CommonUpload(formData); await basicFormRef.value.validate();
options.onSuccess(response, options.file); await propertyFormRef.value.validate();
return response;
} catch (err) {
console.error('上传失败', err);
options.onError(err);
throw err;
}
};
const handleUploadSuccess = (res, file, type) => { const combinedData = {
if (res?.data?.url) { ...formDataProperty.value,
const url = res.data.url; ...formDataBasic.value,
};
switch (type) { const baseRes = await createLand(combinedData);
case 'basic': if (baseRes.code === 200) {
formDataBasic.value.landUrl = url; ElMessage.success('全部信息保存成功');
break; visible.value = false;
case 'property': getData();
formDataProperty.value.propertyCertificateUrl = url; } else {
break; ElMessage.error('信息保存失败');
case 'viewEditBasic':
viewEditFormData.value.landUrl = url;
break;
case 'viewEditProperty':
viewEditFormData.value.propertyCertificateUrl = url;
break;
} }
} catch (error) {
// console.error('保存失败', error);
nextTick(() => {
if (type === 'basic' && basicFormRef.value) {
basicFormRef.value.validateField('landUrl');
} else if (type === 'property' && propertyFormRef.value) {
propertyFormRef.value.validateField('propertyCertificateUrl');
} else if (type.includes('viewEdit') && viewEditFormRef.value) {
const field = type === 'viewEditBasic' ? 'landUrl' : 'propertyCertificateUrl';
viewEditFormRef.value.validateField(field);
}
});
} }
}; };
@ -616,6 +421,11 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
// :deep(.el-dialog__body) {
// padding: 20px;
// height: calc(100vh - 300px);
// overflow-y: auto;
// }
.custom-page { .custom-page {
padding: 20px; padding: 20px;
height: calc(100vh - 150px); height: calc(100vh - 150px);
@ -632,40 +442,40 @@ onMounted(() => {
} }
} }
} }
.tabs-wrapper {
width: 100%;
display: flex;
align-items: center;
// background-color: #7daaaa;
.form-container, :deep(.el-tabs__item) {
.view-edit-form { font-size: 16px;
padding: 0 20px; color: #555555;
max-height: calc(100vh - 300px); font-weight: 500;
overflow-y: auto; // border: 1 solid #f000;
}
.el-form-item { :deep(.el-tabs__content) {
margin-bottom: 22px; padding: 20px;
// background-color: #f5f5f5;
border-radius: 4px;
height: calc(100vh - 300px);
overflow-y: auto;
}
:deep(.el-tab-pane) {
margin: 0 auto;
width: 80%;
} }
} }
.view-edit-form { .form-title {
.section-title { font-size: 16px;
color: #409eff; font-weight: 500;
border-bottom: 1px solid #eee; margin: 30px 0;
padding-bottom: 10px; color: #333333;
margin: 20px 0;
}
.preview-image {
max-width: 100%;
max-height: 200px;
border: 1px solid #eee;
border-radius: 4px;
display: block;
margin-top: 8px;
}
} }
.dialog-footer { .dialog-footer {
display: flex; display: block;
justify-content: flex-end; text-align: center;
padding: 10px 20px 0;
border-top: 1px solid #eee;
} }
</style> </style>

View File

@ -32,6 +32,7 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="地址"> <el-form-item label="地址">
<area-select v-model="localForm.addressArr" :disabled="readonly" :label="null" /> <area-select v-model="localForm.addressArr" :disabled="readonly" :label="null" />
<!-- <el-cascader v-model="localForm.addressArr" :options="options" @change="handleChange" /> -->
</el-form-item> </el-form-item>
<el-form-item label="经营产品"> <el-form-item label="经营产品">
<el-input v-model="localForm.primaryProduct" placeholder="请输入" /> <el-input v-model="localForm.primaryProduct" placeholder="请输入" />
@ -72,6 +73,8 @@ import cloneDeep from 'lodash/cloneDeep';
import { CommonUpload } from '@/apis/index'; import { CommonUpload } from '@/apis/index';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import Attrs from './Attrs.vue'; import Attrs from './Attrs.vue';
import { useCoop } from '../../../../store/modules/coop';
const localForm = useCoop().$state.data;
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -79,7 +82,7 @@ const props = defineProps({
required: true, required: true,
default: () => ({}), default: () => ({}),
}, },
readonly: Boolean, // readonly: Boolean,
}); });
const ossUrl = 'http://gov-cloud.oss-cn-chengdu.aliyuncs.com/'; const ossUrl = 'http://gov-cloud.oss-cn-chengdu.aliyuncs.com/';
@ -89,16 +92,16 @@ const businessLicenceList = ref([]);
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
// props // props
const localForm = ref({ ...props.modelValue }); // const localForm = ref({ ...props.modelValue });
// //
watch( // watch(
localForm, // localForm,
(newVal) => { // (newVal) => {
emit('update:modelValue', { ...newVal }); // emit('update:modelValue', { ...newVal });
}, // },
{ deep: true } // { deep: true }
); // );
// watch( // watch(
// () => props.modelValue, // () => props.modelValue,
// (newVal) => { // (newVal) => {
@ -151,21 +154,21 @@ const handleExceed = () => {
}; };
// //
if (localForm.value.cooperativePhoto) { if (localForm.cooperativePhoto) {
cooperativePhotoList.value = [ cooperativePhotoList.value = [
{ {
name: '合作社照片', name: '合作社照片',
url: ossUrl + localForm.value.cooperativePhoto, url: ossUrl + localForm.cooperativePhoto,
status: 'success', status: 'success',
}, },
]; ];
} }
if (localForm.value.businessLicence) { if (localForm.businessLicence) {
businessLicenceList.value = [ businessLicenceList.value = [
{ {
name: '营业执照', name: '营业执照',
url: ossUrl + localForm.value.businessLicence, url: ossUrl + localForm.businessLicence,
status: 'success', status: 'success',
}, },
]; ];

View File

@ -71,6 +71,8 @@
import { reactive, watch } from 'vue'; import { reactive, watch } from 'vue';
import { Plus } from '@element-plus/icons-vue'; import { Plus } from '@element-plus/icons-vue';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { useCoop } from '../../../../store/modules/coop';
const localForm = useCoop().$state.data;
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -86,23 +88,23 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
// //
const localForm = reactive(cloneDeep(props.modelValue)); // const localForm = reactive(cloneDeep(props.modelValue));
// watch // watch
watch( // watch(
() => localForm, // () => localForm,
(val) => { // (val) => {
emit('update:modelValue', { ...val }); // emit('update:modelValue', { ...val });
}, // },
{ deep: true } // { deep: true }
); // );
watch( // watch(
() => props.modelValue, // () => props.modelValue,
(newVal) => { // (newVal) => {
Object.assign(localForm, cloneDeep(newVal)); // Object.assign(localForm, cloneDeep(newVal));
}, // },
{ deep: true } // { deep: true }
); // );
// //
function downloadTemplate(type) { function downloadTemplate(type) {

View File

@ -28,6 +28,8 @@
<script setup> <script setup>
import { reactive, watch } from 'vue'; import { reactive, watch } from 'vue';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { useCoop } from '../../../../store/modules/coop';
const localForm = useCoop().$state.data;
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -40,22 +42,22 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const localForm = reactive(cloneDeep(props.modelValue)); // const localForm = reactive(cloneDeep(props.modelValue));
watch( // watch(
localForm, // localForm,
(newVal) => { // (newVal) => {
emit('update:modelValue', { ...newVal }); // emit('update:modelValue', { ...newVal });
}, // },
{ deep: true } // { deep: true }
); // );
watch( // watch(
() => props.modelValue, // () => props.modelValue,
(newVal) => { // (newVal) => {
Object.assign(localForm, cloneDeep(newVal)); // Object.assign(localForm, cloneDeep(newVal));
}, // },
{ deep: true } // { deep: true }
); // );
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.credit-evaluation { .credit-evaluation {

View File

@ -105,6 +105,8 @@ import { ref, reactive, watch, onMounted, computed } from 'vue';
import { CRUD_OPTIONS } from '@/config'; import { CRUD_OPTIONS } from '@/config';
import { ElMessageBox, ElMessage } from 'element-plus'; import { ElMessageBox, ElMessage } from 'element-plus';
import { getMemberList, addMember, updateMember, deleteMembers } from '@/apis/businessEntity'; import { getMemberList, addMember, updateMember, deleteMembers } from '@/apis/businessEntity';
import { useCoop } from '../../../../store/modules/coop';
const defaultFormData = useCoop().$state.data;
const dialogTitle = ref('新增'); const dialogTitle = ref('新增');
const dialogVisible = ref(false); const dialogVisible = ref(false);
@ -124,31 +126,31 @@ const searchForm = ref({
}); });
const crudData = ref([]); const crudData = ref([]);
// //
const defaultFormData = { // const defaultFormData = {
id: '', // id: '',
entId: '', // entId: '',
name: '', // name: '',
idType: '101', // idType: '101',
idCard: '', // idCard: '',
sex: '1', // sex: '1',
age: '', // age: '',
phone: '', // phone: '',
provinceCode: '', // // provinceCode: '', //
cityCode: '', // // cityCode: '', //
countyCode: '', // // countyCode: '', //
townCode: '', // // townCode: '', //
street: '', // // street: '', //
addressArr: [], // addressArr: [],
detailAddress: '', // detailAddress: '',
area: '', // area: '',
planCrop: '', // planCrop: '',
planCropName: '', // planCropName: '',
address: '', // address: '',
createTime: '', // createTime: '',
createUser: '', // createUser: '',
updateTime: '', // updateTime: '',
updateUser: '', // updateUser: '',
}; // };
// 使 // 使
const formData = ref({ ...defaultFormData }); const formData = ref({ ...defaultFormData });
const resetForm = () => { const resetForm = () => {
@ -264,18 +266,18 @@ const handleSave = async () => {
} }
}; };
watch( // watch(
() => formData.value.addressArr, // () => formData.value.addressArr,
(newValue) => { // (newValue) => {
if (newValue.length <= 3) { // if (newValue.length <= 3) {
formData.value.provinceCode = '530000'; // formData.value.provinceCode = '530000';
formData.value.cityCode = '530900'; // formData.value.cityCode = '530900';
formData.value.countyCode = newValue[0]; // formData.value.countyCode = newValue[0];
formData.value.townCode = newValue[1]; // formData.value.townCode = newValue[1];
formData.value.street = newValue[2]; // formData.value.street = newValue[2];
} // }
} // }
); // );
const handleAdd = () => { const handleAdd = () => {
isReadonly.value = false; // isReadonly.value = false; //
resetForm(); resetForm();

View File

@ -73,6 +73,8 @@
<script setup> <script setup>
import { reactive, watch } from 'vue'; import { reactive, watch } from 'vue';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { useCoop } from '../../../../store/modules/coop';
const localForm = useCoop().$state.data;
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -86,23 +88,23 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
// props // props
const localForm = reactive(cloneDeep(props.modelValue)); // const localForm = reactive(cloneDeep(props.modelValue));
// //
watch( // watch(
localForm, // localForm,
(newVal) => { // (newVal) => {
emit('update:modelValue', { ...newVal }); // emit('update:modelValue', { ...newVal });
}, // },
{ deep: true } // { deep: true }
); // );
watch( // watch(
() => props.modelValue, // () => props.modelValue,
(newVal) => { // (newVal) => {
Object.assign(localForm, cloneDeep(newVal)); // Object.assign(localForm, cloneDeep(newVal));
}, // },
{ deep: true } // { deep: true }
); // );
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -58,6 +58,9 @@ import TabCreditEvaluation from './components/TabCreditEvaluation.vue';
import TabMember from './components/TabMember.vue'; import TabMember from './components/TabMember.vue';
import { getEnterList, getEnterById, addEnter, updateEnter, approvalEnter, deleteEnter } from '@/apis/businessEntity'; import { getEnterList, getEnterById, addEnter, updateEnter, approvalEnter, deleteEnter } from '@/apis/businessEntity';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { useCoop } from '../../../store/modules/coop';
const useCoopData = useCoop();
// //
const visible = ref(false); const visible = ref(false);
@ -128,6 +131,12 @@ const handleSubmit = async () => {
getData(); // getData(); //
} }
} else if (dialogTitle.value === '编辑') { } else if (dialogTitle.value === '编辑') {
formData.value.provinceCode = formData.value.addressArr[0];
formData.value.cityCode = formData.value.addressArr[1];
formData.value.countyCode = formData.value.addressArr[2];
formData.value.townCode = formData.value.addressArr[3];
formData.value.villageCode = formData.value.addressArr[4];
response = await updateEnter(formData.value); response = await updateEnter(formData.value);
if (response.code === 200) { if (response.code === 200) {
ElMessage.success('编辑成功'); ElMessage.success('编辑成功');
@ -336,6 +345,7 @@ function handleTabChange(tab) {
} }
const handleAdd = () => { const handleAdd = () => {
useCoopData.getData({});
isReadonly.value = false; isReadonly.value = false;
// resetForm(); // resetForm();
dialogTitle.value = '新增'; dialogTitle.value = '新增';
@ -358,6 +368,10 @@ const handleView = async (row) => {
// //
const handleEdit = async (row) => { const handleEdit = async (row) => {
console.log(row);
formData.value = row;
formData.value.addressArr = [row.provinceCode, row.cityCode, row.countyCode, row.townCode, row.villageCode];
useCoopData.getData(formData.value);
loading.value = true; loading.value = true;
dialogTitle.value = '编辑'; dialogTitle.value = '编辑';
try { try {

View File

@ -415,10 +415,4 @@ const onExport = () => {
.dialog-footer { .dialog-footer {
text-align: center; text-align: center;
} }
:deep(.area-cascader-label) {
padding: 0 12px 0 0;
margin: 0;
width: 120px;
box-sizing: border-box;
}
</style> </style>

View File

@ -39,6 +39,12 @@ export function authList(params) {
params, params,
}); });
} }
export function usageList(params) {
return request('brand/applicationrecord/pageByGoodsStatus', {
method: 'GET',
params,
});
}
export function authDetail(id, params) { export function authDetail(id, params) {
return request(`brand/brandbase/detail/${id}`, { return request(`brand/brandbase/detail/${id}`, {
method: 'GET', method: 'GET',
@ -51,6 +57,18 @@ export function getStatisticsData(id, params) {
params, params,
}); });
} }
export function brandUsingSystem(params) {
return request('brand/brandUsingSystem/viewPage', {
method: 'GET',
params,
});
}
export function brandUsingSystemDetail(id, params) {
return request(`brand/brandUsingSystem/detail/${id}`, {
method: 'GET',
params,
});
}
export function saveRecords(data) { export function saveRecords(data) {
return request('brand/applicationrecord/save', { return request('brand/applicationrecord/save', {

View File

@ -502,6 +502,21 @@ export const constantRoutes = [
}, },
], ],
}, },
{
path: 'monitor',
component: BrandLayout,
meta: { title: '使用监管' },
name: 'authUse',
redirect: '/sub-operation-service/brand/monitor',
children: [
{
path: '',
component: () => import('@/views/brand/components/Monitor.vue'),
meta: { title: '使用监管', hideInBread: true },
name: 'brandMonitor',
},
],
},
], ],
}, },

View File

@ -62,7 +62,7 @@
<div class="product-list"> <div class="product-list">
<div v-for="(product, index) in products" :key="product.id" class="product-item" :class="{ 'border-top': index > 0 }"> <div v-for="(product, index) in products" :key="product.id" class="product-item" :class="{ 'border-top': index > 0 }">
<div class="product-info"> <div class="product-info">
<img class="product-img" :src="product.goodsUrl" alt="product" /> <img class="product-img" :src="product.goodsUrl ? product.goodsUrl.split(',')[0] : ''" alt="product" />
<div class="product-text"> <div class="product-text">
<span class="product-name">{{ product.productName }}</span> <span class="product-name">{{ product.productName }}</span>
<div class="detail-item"> <div class="detail-item">
@ -99,8 +99,8 @@
> >
授权证书 授权证书
</el-button> </el-button>
<el-button size="large" class="button" @click="onInspect(product)">溯源报告</el-button> <!-- <el-button size="large" class="button" @click="onInspect(product)">溯源报告</el-button>-->
<el-button v-if="product.status == 2" size="large" class="button" type="danger" @click="onRevoke(product)">取消授权</el-button> <!-- <el-button v-if="product.status == 2" size="large" class="button" type="danger" @click="onRevoke(product)">取消授权</el-button>-->
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,24 +4,17 @@
<!-- 制度列表 --> <!-- 制度列表 -->
<div class="system-list"> <div class="system-list">
<div v-for="(item, index) in filteredList" :key="index" class="system-card" @click="showDetail(item)"> <div v-for="(item, index) in systemList" :key="index" class="system-card" @click="showDetail(item)">
<div class="card-header"> <div class="card-header">
<div class="card-title"> <div class="card-title">
<i class="el-icon-document"></i> <i class="el-icon-document"></i>
{{ item.title }} {{ item.title }}
</div> </div>
<div class="card-meta">发布日期 {{ item.effectiveDate }}</div> <div class="card-meta">发布日期 {{ item.createTime ? item.createTime.split(' ')[0] : '' }}</div>
<!-- <div class="card-meta">-->
<!-- <span>编号{{ item.code }}</span>-->
<!-- </div>-->
</div> </div>
<div class="card-content"> <div class="card-content">
<p>{{ item.desc }}</p> <p>{{ item.descStr }}</p>
</div> </div>
<!-- <div class="card-footer">-->
<!-- <span :class="`status-tag ${item.status === '生效中' ? 'active' : 'inactive'}`">{{ item.status }}</span>-->
<!-- <span>生效日期{{ item.effectiveDate }}</span>-->
<!-- </div>-->
</div> </div>
</div> </div>
@ -34,7 +27,7 @@
<h3>{{ currentDetail.title }}</h3> <h3>{{ currentDetail.title }}</h3>
<div class="dialog-meta"> <div class="dialog-meta">
<!-- <span>编号{{ currentDetail.code }}</span>--> <!-- <span>编号{{ currentDetail.code }}</span>-->
<span :class="`status-tag ${currentDetail.status === '生效中' ? 'active' : 'inactive'}`">{{ currentDetail.status }}</span> <!-- <span :class="`status-tag ${currentDetail.status === 1 ? 'active' : 'inactive'}`">{{ // currentDetail.status === 1 ? '' }}</span>-->
</div> </div>
</div> </div>
<div class="dialog-actions"> <div class="dialog-actions">
@ -45,7 +38,16 @@
</div> </div>
<div class="dialog-content"> <div class="dialog-content">
<!-- 使用Markdown-it渲染的HTML内容 --> <!-- 使用Markdown-it渲染的HTML内容 -->
<div class="doc-preview" v-html="currentDetail.content"></div> <!-- <div class="doc-preview" v-html="currentDetail.content"></div>-->
<div class="doc-preview">
<iframe
v-if="currentDetail.files && currentDetail.files.length > 0"
:src="`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(currentDetail.files ? currentDetail.files[0].fileUrl : '')}`"
width="100%"
height="600px"
frameborder="0"
></iframe>
</div>
</div> </div>
<div class="dialog-footer"> <div class="dialog-footer">
<div class="footer-info"> <div class="footer-info">
@ -60,8 +62,10 @@
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { ElMessage, ElButton } from 'element-plus'; import { ElMessage, ElButton } from 'element-plus';
import { brandUsingSystem } from '@/apis/brand.js';
import { brandUsingSystemDetail } from '../../../apis/brand.js';
// //
const filterTypes = [ const filterTypes = [
@ -84,76 +88,7 @@ const systemList = ref([
title: '《耿马县蔬菜公共品牌申请管理制度》', title: '《耿马县蔬菜公共品牌申请管理制度》',
code: 'BRM-2023-001', code: 'BRM-2023-001',
desc: '包含品牌标识、使用规范、VI应用等内容的最新版本', desc: '包含品牌标识、使用规范、VI应用等内容的最新版本',
content: `\`<div style="font-family: 'Microsoft YaHei', sans-serif; line-height: 1; color: #333; max-width: 800px; margin: 0 auto; padding: 20px;"> content: '',
<div style="text-align: center; font-size: 22px; font-weight: bold; color: #2a5885; border-bottom: 2px solid #2a5885; padding-bottom: 10px;">
耿马县蔬菜公共品牌申请管理制度
</div>
<div style="font-size: 18px; font-weight: bold; margin: 5px 0 15px 0; color: #2a5885; border-bottom: 1px solid #eee; padding-bottom: 5px;">
第一章 总则
</div>
<div style="">
<span style="font-weight: bold; margin-right: 5px; color: #d35400;">第一条</span>
为规范"耿马蔬菜"或其他确定的公共品牌名称公共品牌的使用与管理维护品牌形象提升耿马县蔬菜产品的市场竞争力和附加值促进蔬菜产业高质量发展特制定本制度
</div>
<div style="">
<span style="font-weight: bold; margin-right: 5px; color: #d35400;">第二条</span>
本制度所称"耿马蔬菜"公共品牌以下简称"公共品牌"是指经耿马县人民政府授权由耿马县农业农村局或其他指定主管部门统一注册管理和监督代表耿马县特定区域特定品质蔬菜产品的区域性公用品牌标识
</div>
<div style="">
<span style="font-weight: bold; margin-right: 5px; color: #d35400;">第三条</span>
本制度适用于在耿马县行政区域内从事蔬菜生产加工经营自愿申请使用公共品牌的企业农民专业合作社家庭农场等主体以下简称"申请人"
</div>
<div style="">
<span style="font-weight: bold; margin-right: 5px; color: #d35400;">第四条</span>
耿马县农业农村局是公共品牌的主管部门负责品牌的注册推广授权监督管理和违规处理设立或授权专门的管理机构以下简称"品牌管理机构"具体执行日常管理工作
</div>
<div style="font-size: 18px; font-weight: bold; color: #2a5885; border-bottom: 1px solid #eee; padding-bottom: 5px;">
第二章 申请条件
</div>
<div style="">
<span style="font-weight: bold; margin-right: 5px; color: #d35400;">第五条</span>
申请使用公共品牌的申请人应同时具备以下基本条件
<div style="margin-left: 20px; margin-top: 10px;">
<div style="font-weight: bold; color: #2980b9;">主体合法</div>
<div style="margin-left: 10px;">在耿马县依法注册登记具有独立法人资格或规范的经营主体资格经营状况正常</div>
<div style="font-weight: bold; margin-top: 8px; color: #2980b9;">基地在耿马</div>
<div style="margin-left: 10px;">生产加工基地主要位于耿马县行政区域内</div>
<div style="font-weight: bold; margin-top: 8px; color: #2980b9;">质量达标</div>
<div style="margin-left: 10px;">
<ul style="margin-top: 5px; padding-left: 20px;">
<li>产品符合国家行业及地方相关蔬菜产品质量安全标准</li>
<li>建立并有效运行产品质量安全追溯体系</li>
<li>鼓励通过绿色食品有机农产品或良好农业规范GAP等认证</li>
</ul>
</div>
<div style="font-weight: bold; margin-top: 8px; color: #2980b9;">规模与标准</div>
<div style="margin-left: 10px;">具有一定规模的生产基地生产过程执行耿马县制定的蔬菜标准化生产技术规程</div>
<div style="font-weight: bold; margin-top: 8px; color: #2980b9;">信誉良好</div>
<div style="margin-left: 10px;">近两年内无重大产品质量安全环境污染诚信经营等方面的不良记录和行政处罚</div>
</div>
</div>
<!-- 后续章节保持相同格式 -->
<div style="font-size: 18px; font-weight: bold; color: #2a5885; border-bottom: 1px solid #eee; padding-bottom: 5px;">
第六章 附则
</div>
<div style="">
<span style="font-weight: bold; margin-right: 5px; color: #d35400;">第十五条</span>
本制度自发布之日起施行根据实施情况可适时修订
</div>
</div>\``,
type: 'management', type: 'management',
file: 'BRM-2023-001.docx', file: 'BRM-2023-001.docx',
version: '2.0', version: '2.0',
@ -165,21 +100,26 @@ const systemList = ref([
]); ]);
// //
const filteredList = computed(() => { // const filteredList = computed(() => {
return systemList.value.filter((item) => { // return systemList.value.filter((item) => {
const matchSearch = // const matchSearch =
item.title.toLowerCase().includes(searchQuery.value.toLowerCase()) || // item.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
item.desc.toLowerCase().includes(searchQuery.value.toLowerCase()) || // item.desc.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
item.code.toLowerCase().includes(searchQuery.value.toLowerCase()); // item.code.toLowerCase().includes(searchQuery.value.toLowerCase());
const matchFilter = filterType.value ? item.type === filterType.value : true; // const matchFilter = filterType.value ? item.type === filterType.value : true;
return matchSearch && matchFilter; // return matchSearch && matchFilter;
}); // });
}); // });
// //
const showDetail = (item) => { const showDetail = (item) => {
currentDetail.value = item; brandUsingSystemDetail(item.id).then((res) => {
showDialog.value = true; if (res.code === 200) {
currentDetail.value = res.data;
}
showDialog.value = true;
});
// currentDetail.value = item;
}; };
// //
@ -187,6 +127,18 @@ const hideDetail = () => {
showDialog.value = false; showDialog.value = false;
}; };
onMounted(() => {
getDocList();
});
const getDocList = () => {
brandUsingSystem().then((res) => {
if (res.code === 200) {
systemList.value = res.data.records;
console.log(res);
}
});
};
// //
const downloadDoc = (item) => { const downloadDoc = (item) => {
ElMessage.success({ ElMessage.success({
@ -439,8 +391,6 @@ $color-danger: #ff4949;
white-space: pre-wrap; white-space: pre-wrap;
line-height: 1.5; line-height: 1.5;
background: #ffffff; background: #ffffff;
padding: 20px;
border-radius: 8px;
border: 1px solid $color-border; border: 1px solid $color-border;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
font-family: 'Microsoft Yahei', SimSun, sans-serif; font-family: 'Microsoft Yahei', SimSun, sans-serif;
@ -541,7 +491,6 @@ $color-danger: #ff4949;
// //
.doc-preview { .doc-preview {
line-height: 1.4; // line-height: 1.4; //
padding: 12px 16px; //
h1, h1,
h2, h2,

View File

@ -2,7 +2,7 @@
<div class="usage-monitor"> <div class="usage-monitor">
<!-- 顶部 TabPane居中显示 --> <!-- 顶部 TabPane居中显示 -->
<el-tabs v-model="activeTab" class="tabs-wrapper"> <el-tabs v-model="activeTab" class="tabs-wrapper" @tab-change="changeStatus">
<el-tab-pane label="在售中" name="onSale" /> <el-tab-pane label="在售中" name="onSale" />
<el-tab-pane label="未上架" name="offShelf" /> <el-tab-pane label="未上架" name="offShelf" />
<el-tab-pane label="已失效" name="expired" /> <el-tab-pane label="已失效" name="expired" />
@ -10,15 +10,20 @@
<!-- 列表内容 --> <!-- 列表内容 -->
<div class="list-wrapper"> <div class="list-wrapper">
<div v-for="(p, idx) in filteredProducts" :key="p.id" class="list-item" :class="{ 'has-border': idx > 0 }"> <div v-for="(p, idx) in products" :key="p.id" class="list-item" :class="{ 'has-border': idx > 0 }">
<!-- 商品图 --> <!-- 商品图 -->
<img class="item-img" :src="p.img" alt="商品图" /> <img class="item-img" :src="p.goodsUrl ? p.goodsUrl.split(',')[0] : ''" alt="商品图" />
<!-- 名称 + 月售/库存 --> <!-- 名称 + 月售/库存 -->
<div class="item-info"> <div class="item-info">
<div class="item-name">{{ p.name }}</div> <div class="item-name">{{ p.apiGoodInfoVoDTO ? p.apiGoodInfoVoDTO.goodName : '' }}</div>
<div class="item-stats">月售 {{ p.monthlySales }} · 库存 {{ p.stock }}</div> <div class="item-stats">
<div class="item-price">¥ {{ p.price }} /kg</div> <span>总销售{{ p.apiGoodInfoVoDTO ? p.apiGoodInfoVoDTO.salesVolume : '' }}</span>
<span style="margin-left: 10px">库存 {{ p.apiGoodInfoVoDTO ? p.apiGoodInfoVoDTO.netWeight[0].goodStock : '' }}</span>
</div>
<div class="item-price">
¥ {{ p.apiGoodInfoVoDTO ? p.apiGoodInfoVoDTO.netWeight[0].goodPrice : '' }} /
{{ p.apiGoodInfoVoDTO ? p.apiGoodInfoVoDTO.netWeight[0].unit : '' }}
</div>
</div> </div>
<div class="item-buttom"> <div class="item-buttom">
@ -26,8 +31,8 @@
{{ tabLabels[activeTab] }} {{ tabLabels[activeTab] }}
</div> </div>
<div class="item-actions"> <div class="item-actions">
<el-button size="large" class="button" @click="onInspect(p.id)">抽查</el-button> <el-button size="large" class="button" @click="onInspect(p)">抽查</el-button>
<el-button size="large" class="button" type="danger" @click="onRevoke(p)">取消授权</el-button> <el-button v-if="activeTab !== 'expired'" size="large" class="button" type="danger" @click="onRevoke(p)">取消授权</el-button>
</div> </div>
</div> </div>
</div> </div>
@ -105,7 +110,7 @@
<!-- 右侧图片 --> <!-- 右侧图片 -->
<div class="trace-img"> <div class="trace-img">
<img :src="getAssetsFile(traceData.img)" alt="产品图" /> <img :src="traceData.img" alt="产品图" />
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
@ -113,9 +118,12 @@
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { getAssetsFile } from '@/utils/index.js'; import { getAssetsFile } from '@/utils/index.js';
import { getMonitorList } from '@/apis/brand'; import { getMonitorList } from '@/apis/brand';
import Common from '../../farmingService/components/common.vue';
import { usageList, cancelAuth } from '@/apis/brand.js';
import { ElMessage, ElMessageBox } from 'element-plus';
const activeTab = ref('onSale'); const activeTab = ref('onSale');
const dialogVisible = ref(false); const dialogVisible = ref(false);
@ -163,12 +171,12 @@ const products = ref([
const filteredProducts = computed(() => products.value.filter((p) => p.status === activeTab.value)); const filteredProducts = computed(() => products.value.filter((p) => p.status === activeTab.value));
// id traceData // id traceData
function onInspect(id) { function onInspect(item) {
console.log('查看产品:', id); console.log('查看产品:', item);
// getTraceById(id).then(res=> traceData.value = res) // getTraceById(id).then(res=> traceData.value = res)
const mocks = [ const mocks = [
{ {
productName: '耿马镇沙疆西红柿', productName: '',
quantity: '300KG', quantity: '300KG',
producer: '北大荒技术有限公司', producer: '北大荒技术有限公司',
origin: '耿马县孟定镇下坝村', origin: '耿马县孟定镇下坝村',
@ -183,7 +191,7 @@ function onInspect(id) {
soil: '红壤', soil: '红壤',
}, },
farmingRecords: [ farmingRecords: [
{ date: '2025/3/14', operation: '播种西红柿种', operator: '李强' }, { date: '2025/3/14', operation: '播种', operator: '李强' },
{ date: '2025/4/2', operation: '施肥 氮肥', operator: '李强' }, { date: '2025/4/2', operation: '施肥 氮肥', operator: '李强' },
{ date: '2025/5/17', operation: '浇水', operator: '李强' }, { date: '2025/5/17', operation: '浇水', operator: '李强' },
{ date: '2025/6/14', operation: '采摘', operator: '李强' }, { date: '2025/6/14', operation: '采摘', operator: '李强' },
@ -204,99 +212,63 @@ function onInspect(id) {
time: '2025-4-2 08:13:52', time: '2025-4-2 08:13:52',
buyer: '李楠', buyer: '李楠',
}, },
img: 'images/brand/product4.png', img: '',
},
{
productName: '无土栽培土豆',
quantity: '200KG',
producer: '北大荒技术有限公司',
origin: '耿马县孟定镇下坝村',
productionDate: '2025-1-2',
traceCode: '10.5487542154785XSE254.1040201',
traceCount: 30,
base: {
address: '耿马县孟定镇下坝村',
location: '东经102° · 北纬24°',
area: 12000,
climate: '亚热带高原季风型,温和多雨',
soil: '红壤',
},
farmingRecords: [
{ date: '2025/1/22', operation: '播种 20250102批土豆种', operator: '王岚' },
{ date: '2025/2/14', operation: '施肥 氮肥', operator: '王岚' },
{ date: '2025/3/3', operation: '浇水', operator: '王岚' },
{ date: '2025/4/21', operation: '采摘', operator: '王岚' },
],
packaging: {
company: '瑞禾农产品包装公司',
type: '纸箱',
person: '李桑',
time: '2025-5-2 07:54:14',
},
logistics: {
storageType: '冷藏',
temperature: '2°C',
shipFrom: '北京市朝阳区解放路24号',
shipTo: '上海市黄浦区南京路36号',
},
trade: {
time: '2025-5-16 16:08:35',
buyer: '刘思楠',
},
img: 'images/brand/product6.png',
},
{
productName: '彩椒南瓜混合',
quantity: '200KG',
producer: '北大荒技术有限公司',
origin: '耿马县孟定镇下坝村',
productionDate: '2025-5-15',
traceCode: '10.5487542154785XSE254.1040201',
traceCount: 30,
base: {
address: '耿马县孟定镇下坝村',
location: '东经102° · 北纬24°',
area: 12000,
climate: '亚热带高原季风型,温和多雨',
soil: '红壤',
},
farmingRecords: [
{ date: '2025/2/8', operation: '播种瓜种', operator: '刘琦' },
{ date: '2025/3/14', operation: '施肥 氮肥', operator: '刘琦' },
{ date: '2025/3/20', operation: '浇水', operator: '刘琦' },
{ date: '2025/4/11', operation: '浇水', operator: '刘琦' },
{ date: '2025/5/15', operation: '采摘', operator: '刘琦' },
],
packaging: {
company: '瑞禾农产品包装公司',
type: '纸箱',
person: '王大福',
time: '2025-5-26 14:26:27',
},
logistics: {
storageType: '冷藏',
temperature: '2°C',
shipFrom: '北京市朝阳区解放路24号',
shipTo: '上海市黄浦区南京路36号',
},
trade: {
time: '2025-6-2 11:00:51',
buyer: '刘小花',
},
img: 'images/brand/product1.png',
}, },
]; ];
if (id === 1) { traceData.value = mocks[0];
traceData.value = mocks[0]; traceData.value.productName = item.apiGoodInfoVoDTO.goodName;
} else if (id === 2) { traceData.value.img = item.goodsUrl ? item.goodsUrl.split(',')[0] : '';
traceData.value = mocks[1];
} else if (id === 3) {
traceData.value = mocks[2];
}
dialogVisible.value = true; dialogVisible.value = true;
} }
const onRevoke = (p) => { const onRevoke = (p) => {
console.log('取消授权', p); ElMessageBox.confirm('是否确认取消该商品的授权?', '注意!', {
confirmButtonText: '确认',
cancelButtonText: '放弃',
type: 'error',
})
.then(() => {
cancelAuth({ id: p.id }).then((res) => {
if (res.code === 200) {
ElMessage({
type: 'success',
message: '已取消授权',
});
if (activeTab.value === 'onSale') {
getAuthList(2, 1);
} else if (activeTab.value === 'offShelf') {
getAuthList(2, 3);
} else if (activeTab.value === 'expired') {
getAuthList(4, '');
}
}
});
})
.catch(() => {});
};
const changeStatus = (tab) => {
if (tab === 'onSale') {
getAuthList(2, 1);
} else if (tab === 'offShelf') {
getAuthList(2, 3);
} else if (tab === 'expired') {
getAuthList(4, '');
}
};
const getAuthList = (status, isListed) => {
products.value = [];
let obj = {
status: status,
};
if (isListed) {
obj.goodsIsListed = isListed;
}
usageList(obj).then((res) => {
if (res.code === 200) {
products.value = res.data.records;
}
});
}; };
// //
@ -305,6 +277,10 @@ const statusClass = (tab) => {
if (tab === 'expired') return 'text-danger'; if (tab === 'expired') return 'text-danger';
return 'text-success'; return 'text-success';
}; };
onMounted(() => {
getAuthList(2, 1);
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -3,7 +3,13 @@
<el-container class="brand-layout-container"> <el-container class="brand-layout-container">
<el-aside class="brand-aside-menu"> <el-aside class="brand-aside-menu">
<!-- 菜单部分 --> <!-- 菜单部分 -->
<el-menu v-model:open="openMenus" :default-active="activeMenu" class="brand-aside-menu" @select="handleSelect"> <el-menu
v-model:open="openMenus"
popper-class="no-transition-menu"
:default-active="activeMenu"
class="brand-aside-menu"
@select="handleSelect"
>
<el-menu-item index="apply"> <el-menu-item index="apply">
<img :src="getAssetsFile('images/brand/Apply.png')" class="menu-icon" alt="申请图标" /> <img :src="getAssetsFile('images/brand/Apply.png')" class="menu-icon" alt="申请图标" />
<span>使用申请</span> <span>使用申请</span>
@ -15,6 +21,7 @@
<span>授权管理</span> <span>授权管理</span>
</template> </template>
<el-menu-item index="auth/record"> 授权记录 </el-menu-item> <el-menu-item index="auth/record"> 授权记录 </el-menu-item>
<el-menu-item index="monitor">使用监管</el-menu-item>
<el-menu-item index="auth/manage"> 品牌使用管理 </el-menu-item> <el-menu-item index="auth/manage"> 品牌使用管理 </el-menu-item>
</el-sub-menu> </el-sub-menu>
@ -34,7 +41,7 @@
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { getAssetsFile } from '@/utils/index.js'; import { getAssetsFile } from '@/utils/index.js';
@ -61,6 +68,20 @@ const activeMenu = computed(() => {
function handleSelect(index) { function handleSelect(index) {
router.push(`/sub-operation-service/brand/${index}`); router.push(`/sub-operation-service/brand/${index}`);
} }
onMounted(() => {
const observer = new MutationObserver((mutations) => {
document.querySelectorAll('.el-menu').forEach((menu) => {
menu.classList.remove('el-menu--popup');
menu.style.transition = 'none';
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -89,7 +89,8 @@
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</div> </div>
<div style="display: flex; justify-content: center; flex-wrap: wrap"> <div style="padding: 0 5%; text-indent: 2em; font-size: 18px" v-html="currentGood.describeContent"></div>
<div style="display: flex; justify-content: center; flex-wrap: wrap; margin-top: 20px">
<el-image <el-image
v-for="(item, index) in currentGood.detailUrl" v-for="(item, index) in currentGood.detailUrl"
:key="index" :key="index"

View File

@ -89,7 +89,8 @@
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</div> </div>
<div style="display: flex; justify-content: center; flex-wrap: wrap"> <div style="padding: 0 5%; text-indent: 2em; font-size: 18px" v-html="currentGood.describeContent"></div>
<div style="display: flex; justify-content: center; flex-wrap: wrap; margin-top: 20px">
<el-image <el-image
v-for="(item, index) in currentGood.detailUrl" v-for="(item, index) in currentGood.detailUrl"
:key="index" :key="index"

View File

@ -91,9 +91,9 @@ const toLink = (n, index) => {
// item.icon = item.icon.replace('-1', ''); // item.icon = item.icon.replace('-1', '');
// } // }
// }); // });
console.log('currentIndex.value', currentIndex.value); // console.log('currentIndex.value', currentIndex.value);
router.push(n.path); // router.push(n.path);
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -87,8 +87,8 @@
</div> </div>
</div> </div>
<div class="pagination"> <div class="pagination">
<div style="color: #999999; font-size: 15px">{{ data.total }}</div> <div style="color: #999999; font-size: 15px">{{ total }}</div>
<el-pagination :page-size="20" :pager-count="11" layout="prev, pager, next" :total="data.total" @change="pagination" /> <el-pagination :page-size="20" :pager-count="11" layout="prev, pager, next" :total="total" @change="pagination" />
</div> </div>
</div> </div>
</div> </div>
@ -112,7 +112,7 @@ const route = useRoute();
const router = useRouter(); const router = useRouter();
let number = ref(1); let number = ref(1);
let total = ref(99); let total = ref(0);
let isAll = ref(false); let isAll = ref(false);
let data = ref([]); let data = ref([]);
let page = reactive({ let page = reactive({
@ -157,6 +157,7 @@ let datalist = reactive([
onMounted(() => { onMounted(() => {
shoppingCart(page).then((res) => { shoppingCart(page).then((res) => {
data.value = res.data.records; data.value = res.data.records;
total.value = res.data.total;
addIsCheckProperty(data); addIsCheckProperty(data);
}); });
}); });