From eca47cecf33c93a7cf711f736f9a4d9be48392f9 Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期六, 28 三月 2026 14:41:42 +0800
Subject: [PATCH] feat(enterpriseInfo): 新增企业信息管理页面
---
src/views/basicData/enterpriseInfo/index.vue | 654 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/api/basicData/enterpriseInfo.js | 47 +++
2 files changed, 701 insertions(+), 0 deletions(-)
diff --git a/src/api/basicData/enterpriseInfo.js b/src/api/basicData/enterpriseInfo.js
new file mode 100644
index 0000000..d4c034e
--- /dev/null
+++ b/src/api/basicData/enterpriseInfo.js
@@ -0,0 +1,47 @@
+// 浼佷笟闂ㄦ埛椤甸潰鎺ュ彛
+import request from '@/utils/request'
+
+// 鑾峰彇浼佷笟淇℃伅
+export function getEnterpriseInfo() {
+ return request({
+ url: '/system/enterpriseInfo/getInfo',
+ method: 'get'
+ })
+}
+
+// 淇濆瓨浼佷笟淇℃伅
+export function saveEnterpriseInfo(data) {
+ return request({
+ url: '/system/enterpriseInfo/save',
+ method: 'post',
+ data: data
+ })
+}
+
+// 涓婁紶Logo
+export function uploadLogo(file) {
+ const formData = new FormData()
+ formData.append('file', file)
+ return request({
+ url: '/system/enterpriseInfo/uploadLogo',
+ method: 'post',
+ data: formData,
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
+}
+
+// 涓婁紶浜岀淮鐮�
+export function uploadQrCode(file) {
+ const formData = new FormData()
+ formData.append('file', file)
+ return request({
+ url: '/system/enterpriseInfo/uploadQrCode',
+ method: 'post',
+ data: formData,
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ })
+}
diff --git a/src/views/basicData/enterpriseInfo/index.vue b/src/views/basicData/enterpriseInfo/index.vue
new file mode 100644
index 0000000..1469568
--- /dev/null
+++ b/src/views/basicData/enterpriseInfo/index.vue
@@ -0,0 +1,654 @@
+<template>
+ <div class="app-container">
+ <!-- 椤甸潰鏍囬鏍� -->
+ <div class="page-header">
+ <h2>浼佷笟闂ㄦ埛</h2>
+ <div class="header-actions">
+ <el-button @click="handlePreview" :icon="View" type="info" plain>棰勮</el-button>
+ <el-button @click="toggleEdit" :icon="isEdit ? 'Close' : 'Edit'" :type="isEdit ? 'default' : 'primary'">
+ {{ isEdit ? '鍙栨秷缂栬緫' : '缂栬緫' }}
+ </el-button>
+ </div>
+ </div>
+
+ <!-- 浼佷笟淇℃伅鍗$墖 -->
+ <div class="enterprise-info-card" v-loading="loading">
+ <!-- 鍩烘湰淇℃伅鍖哄煙 -->
+ <div class="info-section">
+ <div class="section-header">
+ <h3>鍩烘湰淇℃伅</h3>
+ </div>
+ <el-descriptions :column="2" border class="info-descriptions">
+ <el-descriptions-item label="鍏徃鍚嶇О">
+ <el-input v-if="isEdit" v-model="form.companyName" placeholder="璇疯緭鍏ュ叕鍙稿悕绉�" clearable />
+ <span v-else class="content-text">{{ form.companyName || '-' }}</span>
+ </el-descriptions-item>
+ <el-descriptions-item label="鑱旂郴浜�">
+ <el-input v-if="isEdit" v-model="form.contactPerson" placeholder="璇疯緭鍏ヨ仈绯讳汉" clearable />
+ <span v-else class="content-text">{{ form.contactPerson || '-' }}</span>
+ </el-descriptions-item>
+ <el-descriptions-item label="鑱旂郴鐢佃瘽">
+ <el-input v-if="isEdit" v-model="form.contactPhone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" clearable />
+ <span v-else class="content-text">{{ form.contactPhone || '-' }}</span>
+ </el-descriptions-item>
+ <el-descriptions-item label="鍏徃鍦板潃">
+ <el-input v-if="isEdit" v-model="form.companyAddress" placeholder="璇疯緭鍏ュ叕鍙稿湴鍧�" clearable />
+ <span v-else class="content-text">{{ form.companyAddress || '-' }}</span>
+ </el-descriptions-item>
+ <el-descriptions-item label="鍏徃缃戠珯">
+ <el-input v-if="isEdit" v-model="form.website" placeholder="璇疯緭鍏ュ叕鍙哥綉绔�" clearable />
+ <a v-else-if="form.website" :href="form.website" target="_blank" class="link-text">{{ form.website }}</a>
+ <span v-else class="content-text">-</span>
+ </el-descriptions-item>
+ </el-descriptions>
+ </div>
+
+ <!-- Logo鍜屼簩缁寸爜鍖哄煙 -->
+ <div class="info-section">
+ <div class="section-header">
+ <h3>浼佷笟鏍囪瘑</h3>
+ </div>
+ <div class="logo-qr-container">
+ <!-- 鍏徃Logo -->
+ <div class="upload-item">
+ <span class="upload-label">鍏徃Logo</span>
+ <div class="upload-wrapper">
+ <el-upload
+ v-if="isEdit"
+ class="logo-uploader"
+ :show-file-list="false"
+ :before-upload="(file) => beforeLogoUpload(file, 'companyLogo')"
+ action="#">
+ <img v-if="form.companyLogo" :src="'/file/preview?url=' + form.companyLogo" class="uploaded-image" />
+ <div v-else class="upload-placeholder">
+ <el-icon class="upload-icon"><Plus /></el-icon>
+ <span class="upload-text">涓婁紶Logo</span>
+ </div>
+ </el-upload>
+ <img
+ v-else-if="form.companyLogo"
+ :src="'/file/preview?url=' + form.companyLogo"
+ class="display-image"
+ />
+ <div v-else class="empty-placeholder">
+ <el-icon :size="40"><Picture /></el-icon>
+ <span>鏆傛棤Logo</span>
+ </div>
+ </div>
+ </div>
+
+ <!-- 浜岀淮鐮� -->
+ <div class="upload-item">
+ <span class="upload-label">浜岀淮鐮�</span>
+ <div class="upload-wrapper">
+ <el-upload
+ v-if="isEdit"
+ class="qr-uploader"
+ :show-file-list="false"
+ :before-upload="(file) => beforeLogoUpload(file, 'qrCode')"
+ action="#">
+ <img v-if="form.qrCode" :src="'/file/preview?url=' + form.qrCode" class="uploaded-image" />
+ <div v-else class="upload-placeholder">
+ <el-icon class="upload-icon"><Plus /></el-icon>
+ <span class="upload-text">涓婁紶浜岀淮鐮�</span>
+ </div>
+ </el-upload>
+ <img
+ v-else-if="form.qrCode"
+ :src="'/file/preview?url=' + form.qrCode"
+ class="display-image"
+ />
+ <div v-else class="empty-placeholder">
+ <el-icon :size="40"><Picture /></el-icon>
+ <span>鏆傛棤浜岀淮鐮�</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 鍏徃绠�浠� -->
+ <div class="info-section">
+ <div class="section-header">
+ <h3>鍏徃绠�浠�</h3>
+ </div>
+ <div class="content-editor">
+ <el-input
+ v-if="isEdit"
+ v-model="form.companyIntro"
+ type="textarea"
+ :rows="6"
+ maxlength="2000"
+ show-word-limit
+ placeholder="璇疯緭鍏ュ叕鍙哥畝浠�..."
+ />
+ <div v-else class="content-display" v-html="form.companyIntro || '<span class=\'empty-text\'>鏆傛棤鍏徃绠�浠�</span>'"></div>
+ </div>
+ </div>
+
+ <!-- 浜у搧浠嬬粛 -->
+ <div class="info-section">
+ <div class="section-header">
+ <h3>浜у搧浠嬬粛</h3>
+ </div>
+ <div class="content-editor">
+ <el-input
+ v-if="isEdit"
+ v-model="form.productIntro"
+ type="textarea"
+ :rows="6"
+ maxlength="2000"
+ show-word-limit
+ placeholder="璇疯緭鍏ヤ骇鍝佷粙缁�..."
+ />
+ <div v-else class="content-display" v-html="form.productIntro || '<span class=\'empty-text\'>鏆傛棤浜у搧浠嬬粛</span>'"></div>
+ </div>
+ </div>
+
+ <!-- 璁惧浠嬬粛 -->
+ <div class="info-section">
+ <div class="section-header">
+ <h3>璁惧浠嬬粛</h3>
+ </div>
+ <div class="content-editor">
+ <el-input
+ v-if="isEdit"
+ v-model="form.equipmentIntro"
+ type="textarea"
+ :rows="6"
+ maxlength="2000"
+ show-word-limit
+ placeholder="璇疯緭鍏ヨ澶囦粙缁�..."
+ />
+ <div v-else class="content-display" v-html="form.equipmentIntro || '<span class=\'empty-text\'>鏆傛棤璁惧浠嬬粛</span>'"></div>
+ </div>
+ </div>
+
+ <!-- 鎿嶄綔鎸夐挳 -->
+ <div v-if="isEdit" class="form-actions">
+ <el-button type="primary" @click="handleSave" :loading="saving" size="large">淇濆瓨</el-button>
+ <el-button @click="handleCancel" size="large">鍙栨秷</el-button>
+ </div>
+ </div>
+
+ <!-- 棰勮寮圭獥 -->
+ <el-dialog
+ v-model="previewVisible"
+ title="浼佷笟淇℃伅棰勮"
+ width="70%"
+ :destroy-on-close="true">
+ <div class="preview-content">
+ <div class="preview-header">
+ <img v-if="form.companyLogo" :src="'/file/preview?url=' + form.companyLogo" class="preview-logo" />
+ <div class="preview-title">
+ <h1>{{ form.companyName || '鍏徃鍚嶇О' }}</h1>
+ <p v-if="form.website">{{ form.website }}</p>
+ </div>
+ </div>
+ <el-divider />
+ <div class="preview-section">
+ <h4>鑱旂郴鏂瑰紡</h4>
+ <p>鑱旂郴浜猴細{{ form.contactPerson || '-' }}</p>
+ <p>鑱旂郴鐢佃瘽锛歿{ form.contactPhone || '-' }}</p>
+ <p>鍏徃鍦板潃锛歿{ form.companyAddress || '-' }}</p>
+ </div>
+ <div class="preview-section">
+ <h4>鍏徃绠�浠�</h4>
+ <div v-html="form.companyIntro || '鏆傛棤绠�浠�'"></div>
+ </div>
+ <div class="preview-section">
+ <h4>浜у搧浠嬬粛</h4>
+ <div v-html="form.productIntro || '鏆傛棤浠嬬粛'"></div>
+ </div>
+ <div class="preview-section">
+ <h4>璁惧浠嬬粛</h4>
+ <div v-html="form.equipmentIntro || '鏆傛棤浠嬬粛'"></div>
+ </div>
+ <div v-if="form.qrCode" class="preview-section preview-qr">
+ <h4>鎵爜鍏虫敞</h4>
+ <img :src="'/file/preview?url=' + form.qrCode" class="qr-image" />
+ </div>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+ import { ref, reactive, onMounted } from 'vue'
+ import { ElMessage } from 'element-plus'
+ import { Plus, Picture, View } from '@element-plus/icons-vue'
+ import { getEnterpriseInfo, saveEnterpriseInfo, uploadLogo, uploadQrCode } from '@/api/basicData/enterpriseInfo.js'
+
+ const { proxy } = getCurrentInstance()
+
+ const loading = ref(false)
+ const saving = ref(false)
+ const isEdit = ref(false)
+ const previewVisible = ref(false)
+ const uploadType = ref('')
+
+ const form = reactive({
+ id: null,
+ companyName: '',
+ companyLogo: '',
+ companyIntro: '',
+ productIntro: '',
+ equipmentIntro: '',
+ contactPerson: '',
+ contactPhone: '',
+ companyAddress: '',
+ website: '',
+ qrCode: ''
+ })
+
+ // 娣辨嫹璐濆師濮嬫暟鎹紝鐢ㄤ簬鍙栨秷鏃舵仮澶�
+ let originalForm = {}
+
+ // 鑾峰彇浼佷笟淇℃伅
+ const fetchInfo = () => {
+ loading.value = true
+ getEnterpriseInfo().then(res => {
+ if (res.code === 200 && res.data) {
+ Object.assign(form, res.data)
+ originalForm = JSON.parse(JSON.stringify(res.data))
+ }
+ }).finally(() => {
+ loading.value = false
+ })
+ }
+
+ // 鍒囨崲缂栬緫妯″紡
+ const toggleEdit = () => {
+ if (isEdit.value) {
+ // 鍙栨秷缂栬緫锛屾仮澶嶅師濮嬫暟鎹�
+ Object.assign(form, originalForm)
+ isEdit.value = false
+ } else {
+ // 杩涘叆缂栬緫妯″紡
+ originalForm = JSON.parse(JSON.stringify(form))
+ isEdit.value = true
+ }
+ }
+
+ // 棰勮
+ const handlePreview = () => {
+ previewVisible.value = true
+ }
+
+ // 鏂囦欢涓婁紶鍓嶆牎楠�
+ const beforeLogoUpload = (file, type) => {
+ const isImage = file.type.startsWith('image/')
+ const isLt2M = file.size / 1024 / 1024 < 2
+
+ if (!isImage) {
+ ElMessage.error('鍙兘涓婁紶鍥剧墖鏂囦欢锛�')
+ return false
+ }
+ if (!isLt2M) {
+ ElMessage.error('鍥剧墖澶у皬涓嶈兘瓒呰繃 2MB锛�')
+ return false
+ }
+
+ uploadType.value = type
+ uploadFileReq(file)
+ return false
+ }
+
+ // 涓婁紶鏂囦欢璇锋眰
+ const uploadFileReq = (file) => {
+ const uploadFn = uploadType.value === 'companyLogo' ? uploadLogo : uploadQrCode
+ uploadFn(file).then(res => {
+ if (res.code === 200) {
+ const path = res.data.tempPath
+ if (uploadType.value === 'companyLogo') {
+ form.companyLogo = path
+ } else {
+ form.qrCode = path
+ }
+ ElMessage.success('涓婁紶鎴愬姛')
+ } else {
+ ElMessage.error(res.msg || '涓婁紶澶辫触')
+ }
+ }).catch(() => {
+ ElMessage.error('涓婁紶澶辫触')
+ })
+ }
+
+ // 淇濆瓨
+ const handleSave = () => {
+ saving.value = true
+ saveEnterpriseInfo(form).then(res => {
+ if (res.code === 200) {
+ ElMessage.success('淇濆瓨鎴愬姛')
+ isEdit.value = false
+ fetchInfo()
+ } else {
+ ElMessage.error(res.msg || '淇濆瓨澶辫触')
+ }
+ }).finally(() => {
+ saving.value = false
+ })
+ }
+
+ // 鍙栨秷
+ const handleCancel = () => {
+ Object.assign(form, originalForm)
+ isEdit.value = false
+ }
+
+ onMounted(() => {
+ fetchInfo()
+ })
+</script>
+
+<style scoped lang="scss">
+.page-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 24px;
+ background: #fff;
+ border-radius: 8px;
+ margin-bottom: 16px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ h2 {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ .header-actions {
+ display: flex;
+ gap: 10px;
+ }
+}
+
+.enterprise-info-card {
+ background: #fff;
+ padding: 24px;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+}
+
+.info-section {
+ margin-bottom: 32px;
+
+ &:last-of-type {
+ margin-bottom: 0;
+ }
+
+ .section-header {
+ margin-bottom: 16px;
+
+ h3 {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ padding-bottom: 10px;
+ border-bottom: 2px solid #409eff;
+ display: inline-block;
+ position: relative;
+
+ &::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ bottom: -2px;
+ width: 60px;
+ height: 2px;
+ background-color: #409eff;
+ }
+ }
+ }
+}
+
+.info-descriptions {
+ :deep(.el-descriptions__label) {
+ width: 120px;
+ background-color: #f5f7fa;
+ font-weight: 500;
+ color: #606266;
+ }
+
+ :deep(.el-descriptions__content) {
+ color: #303133;
+ }
+}
+
+.content-text {
+ color: #606266;
+}
+
+.link-text {
+ color: #409eff;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+.logo-qr-container {
+ display: flex;
+ gap: 40px;
+}
+
+.upload-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ .upload-label {
+ font-size: 14px;
+ color: #606266;
+ margin-bottom: 12px;
+ font-weight: 500;
+ }
+
+ .upload-wrapper {
+ width: 140px;
+ height: 140px;
+ }
+}
+
+/* el-upload 鏍硅妭鐐规槸澶栧眰 div锛岀湡姝hЕ鍙戝尯鍦ㄥ唴灞� .el-upload锛岄渶璁╁唴灞傞摵婊″灞傝櫄绾挎 */
+.logo-uploader,
+.qr-uploader {
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+ width: 140px;
+ height: 140px;
+ border: 1px dashed #d9d9d9;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+ overflow: hidden;
+
+ :deep(.el-upload) {
+ flex: 1;
+ min-height: 0;
+ width: 100%;
+ display: flex !important;
+ flex-direction: column;
+ align-items: stretch;
+ box-sizing: border-box;
+ border: none;
+ outline: none;
+ }
+
+ :deep(.uploaded-image),
+ :deep(.upload-placeholder) {
+ flex: 1 1 auto;
+ min-height: 0;
+ min-width: 0;
+ width: 100%;
+ }
+
+ &:hover {
+ border-color: #409eff;
+ }
+
+ :deep(.uploaded-image) {
+ object-fit: contain;
+ }
+
+ :deep(.upload-placeholder) {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background: #fafafa;
+ color: #8c939d;
+
+ .upload-icon {
+ font-size: 32px;
+ margin-bottom: 8px;
+ }
+
+ .upload-text {
+ font-size: 12px;
+ }
+ }
+}
+
+.display-image {
+ width: 140px;
+ height: 140px;
+ object-fit: contain;
+ border-radius: 8px;
+ border: 1px solid #e4e7ed;
+}
+
+.empty-placeholder {
+ width: 140px;
+ height: 140px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background: #fafafa;
+ border-radius: 8px;
+ border: 1px dashed #e4e7ed;
+ color: #c0c4cc;
+
+ span {
+ margin-top: 8px;
+ font-size: 12px;
+ }
+}
+
+.content-editor {
+ :deep(.el-textarea__inner) {
+ border-radius: 6px;
+ font-size: 14px;
+ line-height: 1.8;
+ }
+}
+
+.content-display {
+ padding: 16px 20px;
+ background: #fafafa;
+ border-radius: 6px;
+ border: 1px solid #ebeef5;
+ line-height: 1.8;
+ color: #606266;
+ min-height: 120px;
+ white-space: pre-wrap;
+
+ :deep(.empty-text) {
+ color: #c0c4cc;
+ font-style: italic;
+ }
+}
+
+.form-actions {
+ display: flex;
+ justify-content: center;
+ gap: 16px;
+ padding-top: 24px;
+ margin-top: 16px;
+ border-top: 1px solid #ebeef5;
+}
+
+/* 棰勮寮圭獥鏍峰紡 */
+.preview-content {
+ .preview-header {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ padding: 20px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 8px;
+ color: #fff;
+ margin-bottom: 20px;
+
+ .preview-logo {
+ width: 80px;
+ height: 80px;
+ object-fit: contain;
+ background: #fff;
+ border-radius: 8px;
+ padding: 8px;
+ }
+
+ .preview-title {
+ h1 {
+ margin: 0 0 8px 0;
+ font-size: 28px;
+ font-weight: 600;
+ }
+
+ p {
+ margin: 0;
+ opacity: 0.9;
+ font-size: 14px;
+ }
+ }
+ }
+
+ .preview-section {
+ margin-bottom: 24px;
+
+ h4 {
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ margin: 0 0 12px 0;
+ padding-bottom: 8px;
+ border-bottom: 1px solid #ebeef5;
+ }
+
+ p {
+ margin: 8px 0;
+ color: #606266;
+ line-height: 1.6;
+ }
+
+ :deep(div) {
+ line-height: 1.8;
+ color: #606266;
+ white-space: pre-wrap;
+ }
+ }
+
+ .preview-qr {
+ text-align: center;
+
+ .qr-image {
+ width: 150px;
+ height: 150px;
+ margin-top: 12px;
+ border-radius: 8px;
+ border: 1px solid #ebeef5;
+ }
+ }
+}
+
+:deep(.el-divider) {
+ margin: 16px 0;
+}
+</style>
--
Gitblit v1.9.3