This commit is contained in:
李志强 2026-01-07 08:43:14 +08:00
parent 09cbd721a0
commit e839da9398
21 changed files with 1953 additions and 159 deletions

View File

@ -0,0 +1,259 @@
# 获取缓存数据 - userinfo 实例
## 方法一:从 localStorage 获取
```javascript
// 获取用户信息
function getUserInfo() {
try {
const userInfoStr = localStorage.getItem('userinfo');
if (userInfoStr) {
const userInfo = JSON.parse(userInfoStr);
return userInfo;
}
return null;
} catch (error) {
console.error('获取用户信息失败:', error);
return null;
}
}
// 使用示例
const userInfo = getUserInfo();
if (userInfo) {
console.log('用户名:', userInfo.username);
console.log('用户ID:', userInfo.id);
console.log('角色:', userInfo.role);
} else {
console.log('未找到用户信息');
}
```
## 方法二:在 Vue 组件中使用
```vue
<template>
<div>
<h3>用户信息</h3>
<p v-if="userInfo">欢迎, {{ userInfo.username }}</p>
<p v-else>请先登录</p>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const userInfo = ref(null);
// 获取用户信息
function getUserInfo() {
try {
const userInfoStr = localStorage.getItem('userinfo');
if (userInfoStr) {
userInfo.value = JSON.parse(userInfoStr);
}
} catch (error) {
console.error('获取用户信息失败:', error);
}
}
onMounted(() => {
getUserInfo();
});
</script>
```
## 方法三:使用工具函数
```javascript
// utils/storage.js
export class StorageUtil {
// 获取用户信息
static getUserInfo() {
try {
const userInfoStr = localStorage.getItem('userinfo');
return userInfoStr ? JSON.parse(userInfoStr) : null;
} catch (error) {
console.error('获取用户信息失败:', error);
return null;
}
}
// 设置用户信息
static setUserInfo(userInfo) {
try {
localStorage.setItem('userinfo', JSON.stringify(userInfo));
} catch (error) {
console.error('保存用户信息失败:', error);
}
}
// 清除用户信息
static clearUserInfo() {
localStorage.removeItem('userinfo');
}
// 检查是否已登录
static isLoggedIn() {
const userInfo = this.getUserInfo();
return userInfo && userInfo.id;
}
}
// 使用示例
import { StorageUtil } from '@/utils/storage';
// 获取用户信息
const userInfo = StorageUtil.getUserInfo();
console.log('用户信息:', userInfo);
// 检查登录状态
if (StorageUtil.isLoggedIn()) {
console.log('用户已登录');
} else {
console.log('用户未登录');
}
```
## 方法四:使用 Pinia store
```javascript
// stores/user.js
import { defineStore } from 'pinia';
import { StorageUtil } from '@/utils/storage';
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null
}),
getters: {
isLoggedIn: (state) => state.userInfo && state.userInfo.id,
username: (state) => state.userInfo?.username || ''
},
actions: {
// 初始化用户信息(从缓存加载)
initUserInfo() {
this.userInfo = StorageUtil.getUserInfo();
},
// 设置用户信息
setUserInfo(userInfo) {
this.userInfo = userInfo;
StorageUtil.setUserInfo(userInfo);
},
// 清除用户信息
clearUserInfo() {
this.userInfo = null;
StorageUtil.clearUserInfo();
},
// 登录
login(userInfo) {
this.setUserInfo(userInfo);
},
// 登出
logout() {
this.clearUserInfo();
}
}
});
// 在组件中使用
import { useUserStore } from '@/stores/user';
export default {
setup() {
const userStore = useUserStore();
// 初始化时加载用户信息
onMounted(() => {
userStore.initUserInfo();
});
return {
userInfo: computed(() => userStore.userInfo),
isLoggedIn: computed(() => userStore.isLoggedIn)
};
}
};
```
## 方法五:使用 sessionStorage
```javascript
// 获取用户信息(仅在当前会话有效)
function getUserInfoFromSession() {
try {
const userInfoStr = sessionStorage.getItem('userinfo');
return userInfoStr ? JSON.parse(userInfoStr) : null;
} catch (error) {
console.error('获取用户信息失败:', error);
return null;
}
}
// 设置用户信息到 sessionStorage
function setUserInfoToSession(userInfo) {
try {
sessionStorage.setItem('userinfo', JSON.stringify(userInfo));
} catch (error) {
console.error('保存用户信息失败:', error);
}
}
// 清除 sessionStorage 中的用户信息
function clearUserInfoFromSession() {
sessionStorage.removeItem('userinfo');
}
```
## 注意事项
1. **数据类型**: 缓存中存储的是字符串,需要使用 `JSON.parse()` 转换为对象
2. **错误处理**: 务必使用 try-catch 处理 JSON 解析错误
3. **数据验证**: 获取到数据后应该验证数据的完整性
4. **安全性**: 敏感信息不要直接存储在前端缓存中
5. **过期处理**: 可以添加时间戳来处理缓存过期逻辑
## 完整示例
```javascript
// 获取用户信息,包含过期检查
function getUserInfoWithExpiry() {
try {
const userInfoStr = localStorage.getItem('userinfo');
if (!userInfoStr) return null;
const userInfo = JSON.parse(userInfoStr);
// 检查是否过期(可选)
if (userInfo.expiry && Date.now() > userInfo.expiry) {
localStorage.removeItem('userinfo');
return null;
}
return userInfo;
} catch (error) {
console.error('获取用户信息失败:', error);
localStorage.removeItem('userinfo'); // 清除损坏的数据
return null;
}
}
// 设置用户信息,包含过期时间
function setUserInfoWithExpiry(userInfo, expiryHours = 24) {
try {
const userInfoWithExpiry = {
...userInfo,
expiry: Date.now() + (expiryHours * 60 * 60 * 1000) // 过期时间
};
localStorage.setItem('userinfo', JSON.stringify(userInfoWithExpiry));
} catch (error) {
console.error('保存用户信息失败:', error);
}
}
```

27
pc/docs/调用字典.md Normal file
View File

@ -0,0 +1,27 @@
````
<template>
{{ (xxxxxxxxDict.find(item => item.dict_value == String(model?.status)) || {}).dict_label || '-' }}
</template>
<script lang="ts" setup>
import { onMounted } from 'vue';
import { useDictStore } from '@/stores/dict';
// 字典store
const dictStore = useDictStore();
const xxxxxxxxDict = ref<any[]>([]);
// 获取审核状态字典
const fetchxxxxxxxxDict = async () => {
try {
xxxxxxxxDict.value = await dictStore.getDictItems('article_status');
console.log(xxxxxxxxDict.value);
} catch (err) {
console.error('获取文章状态字典失败:', err);
}
};
onMounted(() => {
fetchxxxxxxxxDict();
});
</script>
````

View File

@ -52,3 +52,56 @@ export function updateArticleStatus(id, status) {
data: { status }, data: { status },
}); });
} }
// 获取分类列表
export function listCategories(params) {
return request({
url: `/api/categories`,
method: "get",
params,
});
}
// 获取分类详情
export function getCategory(id) {
return request({
url: `/api/categories/${id}`,
method: "get",
});
}
// 创建分类
export function createCategory(data) {
return request({
url: `/api/categories`,
method: "post",
data,
});
}
// 更新分类
export function updateCategory(id, data) {
return request({
url: `/api/categories/${id}`,
method: "put",
data,
});
}
// 删除分类
export function deleteCategory(id) {
return request({
url: `/api/categories/${id}`,
method: "delete",
});
}
// 更新分类状态
export function updateCategoryStatus(id, status) {
return request({
url: `/api/categories/${id}/status`,
method: "patch",
data: { status },
});
}

View File

@ -0,0 +1,581 @@
<template>
<div class="category-manager">
<!-- 工具栏 -->
<div class="toolbar">
<el-button type="primary" @click="handleAdd">新增分类</el-button>
<el-button @click="handleRefresh">刷新</el-button>
<div class="search-wrapper">
<el-input
v-model="searchQuery"
placeholder="搜索分类名称"
clearable
@clear="handleSearch"
style="width: 200px"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
</div>
<!-- 分类菜单 -->
<div class="category-menu" v-loading="loading">
<div v-if="treeData.length === 0" class="empty-state">
<el-empty description="暂无分类数据" />
</div>
<div v-else>
<MenuItem
v-for="category in treeData"
:key="category.id"
:category="category"
:level="0"
@edit="handleEdit"
@add-child="handleAddChild"
@toggle="handleToggle"
@delete="handleDelete"
@enable="handleEnable"
/>
</div>
</div>
<!-- 编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="isEdit ? '编辑分类' : '新增分类'"
width="500px"
>
<el-form
:model="formData"
:rules="formRules"
ref="formRef"
label-width="100px"
>
<el-form-item label="分类名称" prop="label">
<el-input v-model="formData.label" placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="分类值" prop="value">
<el-input
v-model="formData.value"
placeholder="请输入分类值"
:disabled="isEdit"
/>
</el-form-item>
<el-form-item label="父级分类" prop="cid">
<el-select
v-model="formData.cid"
placeholder="选择父级分类"
clearable
filterable
>
<el-option
v-for="category in categoryList.filter(c => c.id !== formData.id)"
:key="category.id"
:label="category.label"
:value="category.id"
/>
</el-select>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="formData.sort"
:min="0"
:max="999"
controls-position="right"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">
确定
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Search, Plus, Edit, Delete, Check, ArrowDown, ArrowRight } from '@element-plus/icons-vue';
import { listCategories, createCategory, updateCategory, updateCategoryStatus } from '@/api/article';
//
const MenuItem = {
name: 'MenuItem',
props: {
category: {
type: Object,
required: true
},
level: {
type: Number,
default: 0
}
},
emits: ['edit', 'add-child', 'toggle', 'delete', 'enable'],
data() {
return {
expanded: true
};
},
computed: {
hasChildren() {
return this.category.children && this.category.children.length > 0;
},
indentStyle() {
return {
paddingLeft: `${this.level * 20 + 12}px`
};
}
},
methods: {
toggleExpanded() {
this.expanded = !this.expanded;
this.$emit('toggle', this.category, this.expanded);
},
handleEdit() {
this.$emit('edit', this.category);
},
handleAddChild() {
this.$emit('add-child', this.category);
},
handleDelete() {
this.$emit('delete', this.category);
},
handleEnable() {
this.$emit('enable', this.category);
}
},
template: `
<div class="menu-item">
<div class="menu-row" :style="indentStyle">
<div class="menu-content">
<div class="menu-header">
<span
v-if="hasChildren"
class="expand-icon"
@click="toggleExpanded"
>
<el-icon>
<component :is="expanded ? 'ArrowDown' : 'ArrowRight'" />
</el-icon>
</span>
<span v-else class="expand-spacer"></span>
<span class="menu-label">{{ category.label }}</span>
<span class="menu-value">({{ category.value }})</span>
<el-tag
:type="category.status === 1 ? 'success' : 'danger'"
size="small"
class="menu-status"
>
{{ category.status === 1 ? '启用' : '禁用' }}
</el-tag>
</div>
<div class="menu-actions">
<el-button
size="small"
type="text"
@click="handleEdit"
>
<el-icon><Edit /></el-icon>
</el-button>
<el-button
size="small"
type="text"
@click="handleAddChild"
>
<el-icon><Plus /></el-icon>
</el-button>
<el-button
v-if="category.status === 1"
size="small"
type="text"
class="danger-btn"
@click="handleDelete"
>
<el-icon><Delete /></el-icon>
</el-button>
<el-button
v-else
size="small"
type="text"
class="success-btn"
@click="handleEnable"
>
<el-icon><Check /></el-icon>
</el-button>
</div>
</div>
</div>
<div v-if="hasChildren && expanded" class="menu-children">
<MenuItem
v-for="child in category.children"
:key="child.id"
:category="child"
:level="level + 1"
@edit="$emit('edit', $event)"
@add-child="$emit('add-child', $event)"
@toggle="$emit('toggle', $event)"
@delete="$emit('delete', $event)"
@enable="$emit('enable', $event)"
/>
</div>
</div>
`
};
//
const loading = ref(false);
const submitLoading = ref(false);
const categoryList = ref([]);
const treeData = ref([]);
const searchQuery = ref('');
const dialogVisible = ref(false);
const isEdit = ref(false);
const formRef = ref(null);
const formData = reactive({
label: '',
value: '',
sort: 0,
status: 1,
cid: 0
});
const formRules = {
label: [
{ required: true, message: '请输入分类名称', trigger: 'blur' },
{ min: 2, max: 50, message: '分类名称长度在 2 到 50 个字符', trigger: 'blur' }
],
value: [
{ required: true, message: '请输入分类值', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9_]+$/, message: '分类值只能包含字母、数字和下划线', trigger: 'blur' }
],
sort: [
{ required: true, message: '请输入排序值', trigger: 'change' }
],
cid: [
{ required: false, message: '请选择父级分类', trigger: 'change' }
]
};
//
function buildTree(data) {
const map = {};
const roots = [];
//
data.forEach(item => {
map[item.id] = { ...item, children: [] };
});
//
data.forEach(item => {
const node = map[item.id];
if (item.cid && item.cid !== 0 && item.cid !== null && map[item.cid]) {
map[item.cid].children.push(node);
} else {
roots.push(node);
}
});
return roots;
}
//
function fetchCategories() {
loading.value = true;
const params = {
keyword: searchQuery.value
};
listCategories(params)
.then((res) => {
const resp = res?.data || res;
if (resp?.code === 0 && resp?.data) {
categoryList.value = resp.data.list || [];
treeData.value = buildTree(resp.data.list || []);
} else {
categoryList.value = [];
treeData.value = [];
}
})
.catch(error => {
console.error('获取分类列表失败:', error);
ElMessage.error('获取分类列表失败');
})
.finally(() => {
loading.value = false;
});
}
//
function handleSearch() {
fetchCategories();
}
//
function handleRefresh() {
searchQuery.value = '';
fetchCategories();
}
//
function handleAdd() {
isEdit.value = false;
Object.assign(formData, {
label: '',
value: '',
sort: 0,
status: 1,
cid: null
});
dialogVisible.value = true;
}
//
function handleEdit(category) {
isEdit.value = true;
Object.assign(formData, {
...category,
cid: category.cid || null
});
dialogVisible.value = true;
}
//
function handleAddChild(parent) {
isEdit.value = false;
Object.assign(formData, {
label: '',
value: '',
sort: 0,
status: 1,
cid: parent.id
});
dialogVisible.value = true;
}
//
function handleToggle(category, expanded) {
//
console.log('Toggle:', category.label, expanded);
}
//
function handleDelete(category) {
ElMessageBox.confirm(
`确定要禁用分类"${category.label}"吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
updateCategoryStatus(category.id, 0)
.then(() => {
ElMessage.success('禁用成功');
fetchCategories();
})
.catch(error => {
console.error('禁用分类失败:', error);
ElMessage.error('禁用分类失败');
});
})
.catch(() => {
//
});
}
//
function handleEnable(category) {
updateCategoryStatus(category.id, 1)
.then(() => {
ElMessage.success('启用成功');
fetchCategories();
})
.catch(error => {
console.error('启用分类失败:', error);
ElMessage.error('启用分类失败');
});
}
//
function handleSubmit() {
formRef.value.validate((valid) => {
if (valid) {
submitLoading.value = true;
const apiCall = isEdit.value
? updateCategory(formData.id, formData)
: createCategory(formData);
apiCall
.then(() => {
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功');
dialogVisible.value = false;
fetchCategories();
})
.catch(error => {
console.error(isEdit.value ? '编辑分类失败:' : '新增分类失败:', error);
ElMessage.error(isEdit.value ? '编辑分类失败' : '新增分类失败');
})
.finally(() => {
submitLoading.value = false;
});
}
});
}
//
onMounted(() => {
fetchCategories();
});
</script>
<style lang="scss" scoped>
.category-manager {
padding: 20px;
.toolbar {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 20px;
.search-wrapper {
margin-left: auto;
}
}
.category-menu {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 8px;
min-height: 300px;
.empty-state {
padding: 60px 20px;
}
}
}
.menu-item {
.menu-row {
padding: 8px 0;
.menu-content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
border-radius: 4px;
transition: background-color 0.2s;
&:hover {
background-color: #f5f7fa;
}
.menu-header {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
.expand-icon, .expand-spacer {
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #c0c4cc;
&:hover {
color: #409eff;
}
}
.expand-spacer {
cursor: default;
}
.menu-label {
font-weight: 500;
color: #303133;
cursor: pointer;
}
.menu-value {
color: #909399;
font-size: 12px;
}
.menu-status {
font-size: 10px;
}
}
.menu-actions {
display: flex;
gap: 4px;
opacity: 0;
transition: opacity 0.2s;
.el-button {
padding: 4px;
color: #909399;
&:hover {
color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
}
.danger-btn:hover {
color: #f56c6c !important;
background-color: rgba(245, 108, 108, 0.1) !important;
}
.success-btn:hover {
color: #67c23a !important;
background-color: rgba(103, 194, 58, 0.1) !important;
}
}
&:hover .menu-actions {
opacity: 1;
}
}
}
.menu-children {
border-left: 2px solid #e4e7ed;
margin-left: 16px;
background-color: #fafbfc;
}
}
:deep(.el-dialog) {
.el-form-item {
margin-bottom: 16px;
}
}
:deep(.el-input-number) {
width: 100%;
}
</style>

View File

@ -28,8 +28,8 @@
{{ formatDate(model.publish_time) }} {{ formatDate(model.publish_time) }}
</span> </span>
<span class="meta-item"> <span class="meta-item">
<el-tag :type="model?.status === 1 ? 'success' : model?.status === 2 ? 'danger' : 'info'"> <el-tag>
{{ model?.status === 1 ? '已发布' : model?.status === 2 ? '已下架' : '草稿' }} {{ (articleStatusDict.find(item => item.dict_value == String(model?.status)) || {}).dict_label || '-' }}
</el-tag> </el-tag>
</span> </span>
</div> </div>
@ -45,8 +45,9 @@
</el-drawer> </el-drawer>
</template> </template>
<script setup> <script lang="ts" setup>
import { ref, watch } from 'vue'; import { ref, watch, onMounted } from 'vue';
import { useDictStore } from '@/stores/dict';
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -63,6 +64,20 @@ const emit = defineEmits(['update:modelValue']);
const visible = ref(false); const visible = ref(false);
// store
const dictStore = useDictStore();
const articleStatusDict = ref<any[]>([]);
//
const fetchArticleStatusDict = async () => {
try {
articleStatusDict.value = await dictStore.getDictItems('article_status');
console.log(articleStatusDict.value);
} catch (err) {
console.error('获取文章状态字典失败:', err);
}
};
// //
watch(() => props.modelValue, (newVal) => { watch(() => props.modelValue, (newVal) => {
visible.value = newVal; visible.value = newVal;
@ -107,6 +122,36 @@ function getCategoryLabel(category) {
}; };
return labels[category] || '其他'; return labels[category] || '其他';
} }
//
function getStatusTagType(status) {
const statusItem = articleStatusDict.value.find(item => item.dict_value === status);
if (statusItem && statusItem.color) {
//
const colorMap = {
'blue': 'primary',
'green': 'success',
'orange': 'warning',
'red': 'danger',
'gray': 'info'
};
return colorMap[statusItem.color] || 'info';
}
//
const statusTypes = {
'0': 'info', // 稿
'1': 'warning', //
'2': 'success', //
'3': 'danger' //
};
return statusTypes[status] || 'info';
}
onMounted(() => {
fetchArticleStatusDict();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -13,6 +13,7 @@
{{ getLabel(customerStatusOptions, String(model.status ?? '')) || ((model.status===1||model.status==='1') ? '正常' : '停用') }} {{ getLabel(customerStatusOptions, String(model.status ?? '')) || ((model.status===1||model.status==='1') ? '正常' : '停用') }}
</el-tag> </el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="经营范围" :span="2">{{ model.business_scope || '-' }}</el-descriptions-item>
<el-descriptions-item label="地址" :span="2">{{ model.address || '-' }}</el-descriptions-item> <el-descriptions-item label="地址" :span="2">{{ model.address || '-' }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<template #footer> <template #footer>

View File

@ -39,8 +39,8 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="联系人" prop="contact"> <el-form-item label="人" prop="contact">
<el-input v-model="form.contact" placeholder="请输入联系人" /> <el-input v-model="form.contact" placeholder="请输入人" />
</el-form-item> </el-form-item>
<el-form-item label="联系电话" prop="phone"> <el-form-item label="联系电话" prop="phone">
<el-input v-model="form.phone" placeholder="请输入联系电话" /> <el-input v-model="form.phone" placeholder="请输入联系电话" />
@ -51,6 +51,14 @@
<el-form-item label="地址" prop="address"> <el-form-item label="地址" prop="address">
<el-input v-model="form.address" type="textarea" placeholder="请输入地址" /> <el-input v-model="form.address" type="textarea" placeholder="请输入地址" />
</el-form-item> </el-form-item>
<el-form-item label="经营范围" prop="business_scope">
<el-input
v-model="form.business_scope"
type="textarea"
:rows="5"
placeholder="请输入经营范围"
/>
</el-form-item>
<el-form-item label="客户状态" prop="status"> <el-form-item label="客户状态" prop="status">
<el-select v-model="form.status" placeholder="请选择客户状态" clearable> <el-select v-model="form.status" placeholder="请选择客户状态" clearable>
<el-option <el-option
@ -73,7 +81,7 @@
import { ref, reactive, watch, computed, onMounted } from 'vue' import { ref, reactive, watch, computed, onMounted } from 'vue'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { createCustomer, updateCustomer } from '../../../../../api/customer.js' import { createCustomer, updateCustomer } from '@/api/customer.js'
import request from '@/utils/request' import request from '@/utils/request'
const props = defineProps<{ modelValue: boolean, isEdit: boolean, model?: any }>() const props = defineProps<{ modelValue: boolean, isEdit: boolean, model?: any }>()
@ -90,13 +98,14 @@ const form = reactive({
id: null as string | null, id: null as string | null,
name: '', name: '',
customer_type: '', customer_type: '',
customer_level: '', customer_level: '3',
industry: '', industry: '',
contact: '', contact: '',
phone: '', phone: '',
email: '', email: '',
address: '', address: '',
status: '' as any business_scope: '',
status: '1'
}) })
const rules: FormRules = { const rules: FormRules = {
@ -104,8 +113,6 @@ const rules: FormRules = {
customer_type: [{ required: true, message: '请选择客户类型', trigger: 'change' }], customer_type: [{ required: true, message: '请选择客户类型', trigger: 'change' }],
customer_level: [{ required: true, message: '请选择客户等级', trigger: 'change' }], customer_level: [{ required: true, message: '请选择客户等级', trigger: 'change' }],
industry: [{ required: true, message: '请选择所属行业', trigger: 'change' }], industry: [{ required: true, message: '请选择所属行业', trigger: 'change' }],
contact: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
phone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
} }
const customerTypeOptions = ref<{label:string,value:string}[]>([]) const customerTypeOptions = ref<{label:string,value:string}[]>([])
@ -142,6 +149,7 @@ watch(() => props.model, (m) => {
form.phone = (m.contact_phone ?? m.phone) ?? '' form.phone = (m.contact_phone ?? m.phone) ?? ''
form.email = (m.contact_email ?? m.email) ?? '' form.email = (m.contact_email ?? m.email) ?? ''
form.address = m.address ?? '' form.address = m.address ?? ''
form.business_scope = m.business_scope ?? ''
form.status = m.status != null && m.status !== undefined ? String(m.status) : '' form.status = m.status != null && m.status !== undefined ? String(m.status) : ''
} else { } else {
resetForm() resetForm()
@ -152,13 +160,14 @@ function resetForm() {
form.id = null form.id = null
form.name = '' form.name = ''
form.customer_type = '' form.customer_type = ''
form.customer_level = '' form.customer_level = '3'
form.industry = '' form.industry = ''
form.contact = '' form.contact = ''
form.phone = '' form.phone = ''
form.email = '' form.email = ''
form.address = '' form.address = ''
form.status = '' form.business_scope = ''
form.status = '1'
formRef.value?.clearValidate() formRef.value?.clearValidate()
} }
@ -187,6 +196,7 @@ async function onSubmit() {
contact_phone: form.phone, contact_phone: form.phone,
contact_email: form.email, contact_email: form.email,
address: form.address, address: form.address,
business_scope: form.business_scope,
status: String(form.status), status: String(form.status),
tenant_id: tenantId, tenant_id: tenantId,
} }

View File

@ -16,22 +16,23 @@
<!-- 客户列表 --> <!-- 客户列表 -->
<el-table :data="customerList" v-loading="loading" stripe border style="width: 100%"> <el-table :data="customerList" v-loading="loading" stripe border style="width: 100%">
<el-table-column prop="name" label="客户名称" width="220" /> <el-table-column prop="name" label="客户名称" width="360" fixed="left">
<el-table-column prop="contact" label="联系人" width="100" />
<el-table-column prop="phone" label="联系电话" min-width="120" />
<el-table-column prop="email" label="邮箱" min-width="150" />
<el-table-column prop="address" label="地址" min-width="150" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'info'"> <div style="display: flex; align-items: center; gap: 8px;">
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
{{ row.status === 1 ? '正常' : '停用' }} {{ row.status === 1 ? '正常' : '停用' }}
</el-tag> </el-tag>
<el-link type="primary" @click="handleView(row)">{{ row.name }}</el-link>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="300" fixed="right" align="center"> <el-table-column prop="contact" label="法人" width="100" />
<el-table-column prop="phone" label="联系电话" min-width="160" />
<el-table-column prop="email" label="邮箱" min-width="180" />
<el-table-column prop="address" label="地址" min-width="300" show-overflow-tooltip />
<el-table-column label="操作" width="250" fixed="right" align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="info" @click="handleContactView(row)">联系人</el-button> <el-button link type="info" @click="handleContactView(row)">联系人</el-button>
<el-button link type="info" @click="handleView(row)">详情</el-button>
<el-button link type="info" @click="handleInvoiceView(row)">开票信息</el-button> <el-button link type="info" @click="handleInvoiceView(row)">开票信息</el-button>
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button> <el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button> <el-button link type="danger" @click="handleDelete(row)">删除</el-button>

View File

@ -13,6 +13,7 @@
{{ getDictLabel('supplier_status', model.status) || ((model.status===1||model.status==='1') ? '正常' : '停用') }} {{ getDictLabel('supplier_status', model.status) || ((model.status===1||model.status==='1') ? '正常' : '停用') }}
</el-tag> </el-tag>
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="经营范围" :span="2">{{ model.business_scope || '-' }}</el-descriptions-item>
<el-descriptions-item label="地址" :span="2">{{ model.address || '-' }}</el-descriptions-item> <el-descriptions-item label="地址" :span="2">{{ model.address || '-' }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<template #footer> <template #footer>

View File

@ -24,8 +24,8 @@
<el-option v-for="item in industryOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in industryOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="联系人" prop="contact"> <el-form-item label="人" prop="contact">
<el-input v-model="form.contact" placeholder="请输入联系人" /> <el-input v-model="form.contact" placeholder="请输入人" />
</el-form-item> </el-form-item>
<el-form-item label="联系电话" prop="phone"> <el-form-item label="联系电话" prop="phone">
<el-input v-model="form.phone" placeholder="请输入联系电话" /> <el-input v-model="form.phone" placeholder="请输入联系电话" />
@ -36,6 +36,14 @@
<el-form-item label="地址" prop="address"> <el-form-item label="地址" prop="address">
<el-input v-model="form.address" type="textarea" placeholder="请输入地址" /> <el-input v-model="form.address" type="textarea" placeholder="请输入地址" />
</el-form-item> </el-form-item>
<el-form-item label="经营范围" prop="business_scope">
<el-input
v-model="form.business_scope"
type="textarea"
:rows="5"
placeholder="请输入经营范围"
/>
</el-form-item>
<el-form-item label="供应商状态" prop="status"> <el-form-item label="供应商状态" prop="status">
<el-select v-model="form.status" placeholder="请选择供应商状态" clearable> <el-select v-model="form.status" placeholder="请选择供应商状态" clearable>
<el-option v-for="item in supplierStatusOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in supplierStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
@ -53,7 +61,7 @@
import { ref, reactive, watch, computed, onMounted } from 'vue' import { ref, reactive, watch, computed, onMounted } from 'vue'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { createSupplier, updateSupplier } from '../../../../../api/supplier.js' import { createSupplier, updateSupplier } from '@/api/supplier.js'
import request from '@/utils/request' import request from '@/utils/request'
const props = defineProps<{ modelValue: boolean, isEdit: boolean, model?: any }>() const props = defineProps<{ modelValue: boolean, isEdit: boolean, model?: any }>()
@ -70,13 +78,14 @@ const form = reactive({
id: null as string | null, id: null as string | null,
name: '', name: '',
supplier_type: '', supplier_type: '',
supplier_level: '', supplier_level: '3',
industry: '', industry: '',
contact: '', contact: '',
phone: '', phone: '',
email: '', email: '',
address: '', address: '',
status: '' as any business_scope: '',
status: '1'
}) })
const rules: FormRules = { const rules: FormRules = {
@ -122,6 +131,7 @@ watch(() => props.model, (m) => {
form.phone = (m.contact_phone ?? m.phone) ?? '' form.phone = (m.contact_phone ?? m.phone) ?? ''
form.email = (m.contact_email ?? m.email) ?? '' form.email = (m.contact_email ?? m.email) ?? ''
form.address = m.address ?? '' form.address = m.address ?? ''
form.business_scope = m.business_scope ?? ''
form.status = m.status != null && m.status !== undefined ? String(m.status) : '' form.status = m.status != null && m.status !== undefined ? String(m.status) : ''
} else { } else {
resetForm() resetForm()
@ -132,13 +142,14 @@ function resetForm() {
form.id = null form.id = null
form.name = '' form.name = ''
form.supplier_type = '' form.supplier_type = ''
form.supplier_level = '' form.supplier_level = '3'
form.industry = '' form.industry = ''
form.contact = '' form.contact = ''
form.phone = '' form.phone = ''
form.email = '' form.email = ''
form.address = '' form.address = ''
form.status = '' form.business_scope = ''
form.status = '1'
formRef.value?.clearValidate() formRef.value?.clearValidate()
} }
@ -167,6 +178,7 @@ async function onSubmit() {
contact_phone: form.phone, contact_phone: form.phone,
contact_email: form.email, contact_email: form.email,
address: form.address, address: form.address,
business_scope: form.business_scope,
status: String(form.status), status: String(form.status),
tenant_id: tenantId, tenant_id: tenantId,
} }

View File

@ -27,22 +27,23 @@
border border
style="width: 100%" style="width: 100%"
> >
<el-table-column prop="supplier_name" label="供应商名称" width="220" /> <el-table-column prop="supplier_name" label="供应商名称" width="360" fixed="left">
<el-table-column prop="contact_person" label="联系人" width="140" />
<el-table-column prop="contact_phone" label="联系电话" width="160" />
<el-table-column prop="contact_email" label="邮箱" width="220" />
<el-table-column prop="address" label="地址" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="row.status === '1' ? 'success' : 'info'"> <div style="display: flex; align-items: center; gap: 8px;">
<el-tag :type="row.status === '1' ? 'success' : 'info'" size="small">
{{ row.status === '1' ? '正常' : '停用' }} {{ row.status === '1' ? '正常' : '停用' }}
</el-tag> </el-tag>
<el-link type="primary" @click="handleView(row)">{{ row.supplier_name }}</el-link>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="300" fixed="right"> <el-table-column prop="contact_person" label="法人" width="100" />
<el-table-column prop="contact_phone" label="联系电话" width="160" />
<el-table-column prop="contact_email" label="邮箱" min-width="180" />
<el-table-column prop="address" label="地址" min-width="300" show-overflow-tooltip />
<el-table-column label="操作" width="250" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="info" @click="handleContactView(row)">联系人</el-button> <el-button link type="info" @click="handleContactView(row)">联系人</el-button>
<el-button link type="info" @click="handleView(row)">详情</el-button>
<el-button link type="info" @click="handleInvoiceView(row)">开票信息</el-button> <el-button link type="info" @click="handleInvoiceView(row)">开票信息</el-button>
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button> <el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button> <el-button link type="danger" @click="handleDelete(row)">删除</el-button>

View File

@ -449,3 +449,362 @@ func (c *ArticlesController) UpdateArticleStatus() {
} }
c.ServeJSON() c.ServeJSON()
} }
// ListCategories 获取分类列表
func (c *ArticlesController) ListCategories() {
// 获取查询参数
tenantId, _ := c.GetInt("tenantId")
page, _ := c.GetInt("page", 1)
pageSize, _ := c.GetInt("pageSize", 20)
if pageSizeStr := c.GetString("size"); pageSizeStr != "" {
if size, err := strconv.Atoi(pageSizeStr); err == nil && size > 0 {
pageSize = size
}
}
keyword := c.GetString("keyword")
statusStr := c.GetString("status")
// 状态过滤:默认为启用状态(1)只有明确传递status参数时才按指定状态过滤
var status *int8
if statusStr != "" {
// 如果明确传递了status参数按指定值过滤
if s, err := strconv.Atoi(statusStr); err == nil {
statusVal := int8(s)
status = &statusVal
}
} else {
// 如果没有传递status参数默认只显示启用状态
statusVal := int8(1)
status = &statusVal
}
// 调用服务层获取分类列表
categories, total, err := services.GetArticleCategories(tenantId, page, pageSize, keyword, status)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取分类列表失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 格式化返回数据
categoryList := make([]map[string]interface{}, 0)
for _, category := range categories {
categoryList = append(categoryList, map[string]interface{}{
"id": category.Id,
"label": category.DictLabel,
"value": category.DictValue,
"status": category.Status,
"sort": category.Sort,
"color": category.Color,
"icon": category.Icon,
"remark": category.Remark,
"create_time": category.CreateTime,
"update_time": category.UpdateTime,
})
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "ok",
"data": map[string]interface{}{
"list": categoryList,
"total": total,
},
}
c.ServeJSON()
}
// GetCategory 获取分类详情
func (c *ArticlesController) GetCategory() {
// 从URL获取分类ID
categoryId, err := c.GetInt(":id")
if err != nil || categoryId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "无效的分类ID",
"data": nil,
}
c.ServeJSON()
return
}
// 调用服务层获取分类详情
category, err := services.GetArticleCategoryById(categoryId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "获取分类详情失败: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
if category == nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "分类不存在",
"data": nil,
}
c.ServeJSON()
return
}
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "获取分类详情成功",
"data": map[string]interface{}{
"id": category.Id,
"label": category.DictLabel,
"value": category.DictValue,
"status": category.Status,
"sort": category.Sort,
"color": category.Color,
"icon": category.Icon,
"remark": category.Remark,
"create_time": category.CreateTime,
"update_time": category.UpdateTime,
},
}
c.ServeJSON()
}
// CreateCategory 创建分类
func (c *ArticlesController) CreateCategory() {
// 定义接收分类数据的结构体
var categoryData struct {
Label string `json:"label"`
Value string `json:"value"`
Status int8 `json:"status"`
Sort int `json:"sort"`
Color string `json:"color"`
Icon string `json:"icon"`
Remark string `json:"remark"`
}
// 解析请求体JSON数据
err := json.Unmarshal(c.Ctx.Input.RequestBody, &categoryData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数格式错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 校验必要参数
if categoryData.Label == "" {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "分类名称不能为空",
"data": nil,
}
c.ServeJSON()
return
}
if categoryData.Value == "" {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "分类值不能为空",
"data": nil,
}
c.ServeJSON()
return
}
// 从URL参数或JWT获取租户ID优先使用URL参数
tenantId, _ := c.GetInt("tenant_id", 0)
if tenantId == 0 {
tenantId, _ = c.GetInt("tenantId", 0)
}
// 从JWT上下文中获取用户ID
userId, _ := c.GetInt("userId", 0)
// 调用服务层创建分类
itemId, err := services.CreateArticleCategory(tenantId, userId, categoryData.Label, categoryData.Value, categoryData.Status, categoryData.Sort, categoryData.Color, categoryData.Icon, categoryData.Remark)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "创建分类失败: " + err.Error(),
"data": nil,
}
} else {
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "创建分类成功",
"data": map[string]interface{}{
"id": itemId,
},
}
}
c.ServeJSON()
}
// UpdateCategory 更新分类
func (c *ArticlesController) UpdateCategory() {
// 从URL获取分类ID
categoryId, err := c.GetInt(":id")
if err != nil || categoryId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "无效的分类ID",
"data": nil,
}
c.ServeJSON()
return
}
// 定义接收更新数据的结构体
var categoryData struct {
Label string `json:"label"`
Value string `json:"value"`
Status int8 `json:"status"`
Sort int `json:"sort"`
Color string `json:"color"`
Icon string `json:"icon"`
Remark string `json:"remark"`
}
// 解析请求体JSON数据
err = json.Unmarshal(c.Ctx.Input.RequestBody, &categoryData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数格式错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 校验必要参数
if categoryData.Label == "" {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "分类名称不能为空",
"data": nil,
}
c.ServeJSON()
return
}
if categoryData.Value == "" {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "分类值不能为空",
"data": nil,
}
c.ServeJSON()
return
}
// 从JWT上下文中获取用户ID
userId, _ := c.GetInt("userId", 0)
// 调用服务层更新分类
err = services.UpdateArticleCategory(categoryId, userId, categoryData.Label, categoryData.Value, categoryData.Status, categoryData.Sort, categoryData.Color, categoryData.Icon, categoryData.Remark)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "更新分类失败: " + err.Error(),
"data": nil,
}
} else {
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "更新分类成功",
"data": nil,
}
}
c.ServeJSON()
}
// DeleteCategory 删除分类
func (c *ArticlesController) DeleteCategory() {
// 从URL获取分类ID
categoryId, err := c.GetInt(":id")
if err != nil || categoryId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "无效的分类ID",
"data": nil,
}
c.ServeJSON()
return
}
// 调用服务层删除分类
err = services.DeleteArticleCategory(categoryId)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "删除分类失败: " + err.Error(),
"data": nil,
}
} else {
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "删除分类成功",
"data": nil,
}
}
c.ServeJSON()
}
// UpdateCategoryStatus 更新分类状态
func (c *ArticlesController) UpdateCategoryStatus() {
// 从URL获取分类ID
categoryId, err := c.GetInt(":id")
if err != nil || categoryId <= 0 {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "无效的分类ID",
"data": nil,
}
c.ServeJSON()
return
}
// 定义接收状态数据的结构体
var statusData struct {
Status int8 `json:"status"`
}
// 解析请求体JSON数据
err = json.Unmarshal(c.Ctx.Input.RequestBody, &statusData)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "请求参数格式错误: " + err.Error(),
"data": nil,
}
c.ServeJSON()
return
}
// 调用服务层更新分类状态
err = services.UpdateArticleCategoryStatus(categoryId, statusData.Status)
if err != nil {
c.Data["json"] = map[string]interface{}{
"code": 1,
"message": "更新分类状态失败: " + err.Error(),
"data": nil,
}
} else {
c.Data["json"] = map[string]interface{}{
"code": 0,
"message": "更新分类状态成功",
"data": nil,
}
}
c.ServeJSON()
}

View File

@ -8,8 +8,8 @@ import (
"server/models" "server/models"
"server/services" "server/services"
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web"
) )
type CustomerController struct { type CustomerController struct {
@ -45,7 +45,33 @@ func (c *CustomerController) Detail() {
if err != nil { if err != nil {
c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()} c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()}
} else { } else {
c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok", "data": m} // 确保business_scope字段被正确返回
result := map[string]interface{}{
"id": m.Id,
"tenant_id": m.TenantId,
"customer_name": m.CustomerName,
"customer_type": m.CustomerType,
"contact_person": m.ContactPerson,
"contact_phone": m.ContactPhone,
"contact_email": m.ContactEmail,
"customer_level": m.CustomerLevel,
"industry": m.Industry,
"address": m.Address,
"register_time": m.RegisterTime,
"expire_time": m.ExpireTime,
"status": m.Status,
"remark": m.Remark,
"business_scope": m.BusinessScope, // 确保包含business_scope字段
"invoice_title": m.InvoiceTitle,
"tax_number": m.TaxNumber,
"bank_name": m.BankName,
"bank_account": m.BankAccount,
"registered_address": m.RegisteredAddress,
"registered_phone": m.RegisteredPhone,
"create_time": m.CreateTime,
"update_time": m.UpdateTime,
}
c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok", "data": result}
} }
c.ServeJSON() c.ServeJSON()
} }
@ -68,62 +94,70 @@ func (c *CustomerController) Add() {
// Edit POST /api/crm/customer/edit body: {id, ...} // Edit POST /api/crm/customer/edit body: {id, ...}
func (c *CustomerController) Edit() { func (c *CustomerController) Edit() {
var body map[string]interface{} var payload map[string]interface{}
_ = json.Unmarshal(c.Ctx.Input.RequestBody, &body) if err := json.Unmarshal(c.Ctx.Input.RequestBody, &payload); err != nil {
id, _ := body["id"].(string) c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数格式错误"}
if id == "" { c.ServeJSON()
// 也允许前端直接在JSON中传 id 字段,下面会再从结构体取 return
} }
var m models.Customer
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &m); err != nil { // Extract and validate id
// 回退从通用map中提取已知字段避免类型不匹配导致失败 var idStr string
if body == nil { if idVal, ok := payload["id"]; ok {
_ = json.Unmarshal(c.Ctx.Input.RequestBody, &body) switch v := idVal.(type) {
} case float64:
toStr := func(v interface{}) string { idStr = strconv.Itoa(int(v))
switch t := v.(type) { case int:
case nil: idStr = strconv.Itoa(v)
return ""
case string: case string:
return t idStr = v
case json.Number:
return t.String()
default: default:
return fmt.Sprint(v) idStr = fmt.Sprintf("%v", v)
} }
} }
m = models.Customer{ if idStr == "" {
Id: toStr(body["id"]),
TenantId: toStr(body["tenant_id"]),
CustomerName: toStr(body["customer_name"]),
CustomerType: toStr(body["customer_type"]),
ContactPerson: toStr(body["contact_person"]),
ContactPhone: toStr(body["contact_phone"]),
ContactEmail: toStr(body["contact_email"]),
CustomerLevel: toStr(body["customer_level"]),
Industry: toStr(body["industry"]),
Address: toStr(body["address"]),
Status: toStr(body["status"]),
Remark: toStr(body["remark"]),
}
if m.Id == "" {
if id == "" {
c.Data["json"] = map[string]interface{}{"code": 1, "message": "id不能为空"} c.Data["json"] = map[string]interface{}{"code": 1, "message": "id不能为空"}
c.ServeJSON() c.ServeJSON()
return return
} }
m.Id = id
// Build update params - only include fields that are provided
params := orm.Params{}
fieldMappings := map[string]string{
"customer_name": "CustomerName",
"customer_type": "CustomerType",
"customer_level": "CustomerLevel",
"industry": "Industry",
"contact_person": "ContactPerson",
"contact_phone": "ContactPhone",
"contact_email": "ContactEmail",
"address": "Address",
"register_time": "RegisterTime",
"expire_time": "ExpireTime",
"status": "Status",
"remark": "Remark",
"business_scope": "BusinessScope",
"invoice_title": "InvoiceTitle",
"tax_number": "TaxNumber",
"bank_name": "BankName",
"bank_account": "BankAccount",
"registered_address": "RegisteredAddress",
"registered_phone": "RegisteredPhone",
}
for jsonKey := range fieldMappings {
if value, exists := payload[jsonKey]; exists {
params[jsonKey] = value
} }
} }
if m.Id == "" {
if id == "" { if len(params) == 0 {
c.Data["json"] = map[string]interface{}{"code": 1, "message": "id不能为空"} c.Data["json"] = map[string]interface{}{"code": 1, "message": "没有要更新的字段"}
c.ServeJSON() c.ServeJSON()
return return
} }
m.Id = id
} if err := services.UpdateCustomerFields(idStr, params); err != nil {
if err := services.UpdateCustomer(&m); err != nil {
c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()} c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()}
} else { } else {
c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok"} c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok"}
@ -157,30 +191,42 @@ func (c *CustomerController) Delete() {
// 更新客户开票信息 // 更新客户开票信息
func (c *CustomerController) UpdateInvoice() { func (c *CustomerController) UpdateInvoice() {
var body struct { // Extract data from the request payload
Id string `json:"id"` var payload map[string]interface{}
TenantId string `json:"tenantId"` if err := json.Unmarshal(c.Ctx.Input.RequestBody, &payload); err != nil {
}
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &body); err != nil {
c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数格式错误"} c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数格式错误"}
c.ServeJSON() c.ServeJSON()
return return
} }
if body.Id == "" {
// Extract and validate id
var idStr string
if idVal, ok := payload["id"]; ok {
switch v := idVal.(type) {
case float64:
idStr = strconv.Itoa(int(v))
case int:
idStr = strconv.Itoa(v)
case string:
idStr = v
default:
idStr = fmt.Sprintf("%v", v)
}
}
if idStr == "" {
c.Data["json"] = map[string]interface{}{"code": 1, "message": "id不能为空"} c.Data["json"] = map[string]interface{}{"code": 1, "message": "id不能为空"}
c.ServeJSON() c.ServeJSON()
return return
} }
// Extract invoice fields from the request payload
var payload map[string]interface{} // Extract invoice fields from the payload
_ = json.Unmarshal(c.Ctx.Input.RequestBody, &payload)
params := orm.Params{} params := orm.Params{}
for _, key := range []string{"invoice_title", "tax_number", "bank_name", "bank_account", "registered_address", "registered_phone"} { for _, key := range []string{"invoice_title", "tax_number", "bank_name", "bank_account", "registered_address", "registered_phone"} {
if v, ok := payload[key]; ok { if v, ok := payload[key]; ok {
params[key] = v params[key] = v
} }
} }
if err := services.UpdateInvoice(body.Id, params); err != nil { if err := services.UpdateInvoice(idStr, params); err != nil {
c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()} c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()}
} else { } else {
c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok"} c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok"}

View File

@ -2,13 +2,14 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt"
"strconv" "strconv"
"server/models" "server/models"
"server/services" "server/services"
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web"
) )
type SupplierController struct { type SupplierController struct {
@ -47,7 +48,33 @@ func (c *SupplierController) Detail() {
if err != nil { if err != nil {
c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()} c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()}
} else { } else {
c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok", "data": m} // 确保business_scope字段被正确返回
result := map[string]interface{}{
"id": m.Id,
"tenant_id": m.TenantId,
"supplier_name": m.SupplierName,
"supplier_type": m.SupplierType,
"contact_person": m.ContactPerson,
"contact_phone": m.ContactPhone,
"contact_email": m.ContactEmail,
"supplier_level": m.SupplierLevel,
"industry": m.Industry,
"address": m.Address,
"register_time": m.RegisterTime,
"expire_time": m.ExpireTime,
"status": m.Status,
"remark": m.Remark,
"business_scope": m.BusinessScope, // 确保包含business_scope字段
"invoice_title": m.InvoiceTitle,
"tax_number": m.TaxNumber,
"bank_name": m.BankName,
"bank_account": m.BankAccount,
"registered_address": m.RegisteredAddress,
"registered_phone": m.RegisteredPhone,
"create_time": m.CreateTime,
"update_time": m.UpdateTime,
}
c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok", "data": result}
} }
c.ServeJSON() c.ServeJSON()
} }
@ -68,31 +95,70 @@ func (c *SupplierController) Add() {
} }
func (c *SupplierController) Edit() { func (c *SupplierController) Edit() {
var body map[string]interface{} var payload map[string]interface{}
_ = json.Unmarshal(c.Ctx.Input.RequestBody, &body) if err := json.Unmarshal(c.Ctx.Input.RequestBody, &payload); err != nil {
var idInt int64
if v, ok := body["id"].(string); ok && v != "" {
if n, err := strconv.ParseInt(v, 10, 64); err == nil {
idInt = n
}
} else if v2, ok2 := body["id"].(float64); ok2 {
idInt = int64(v2)
}
var m models.Supplier
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &m); err != nil {
c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数格式错误"} c.Data["json"] = map[string]interface{}{"code": 1, "message": "请求参数格式错误"}
c.ServeJSON() c.ServeJSON()
return return
} }
if m.Id == 0 {
if idInt == 0 { // Extract and validate id
var idStr string
if idVal, ok := payload["id"]; ok {
switch v := idVal.(type) {
case float64:
idStr = strconv.Itoa(int(v))
case int:
idStr = strconv.Itoa(v)
case string:
idStr = v
default:
idStr = fmt.Sprintf("%v", v)
}
}
if idStr == "" {
c.Data["json"] = map[string]interface{}{"code": 1, "message": "id不能为空"} c.Data["json"] = map[string]interface{}{"code": 1, "message": "id不能为空"}
c.ServeJSON() c.ServeJSON()
return return
} }
m.Id = idInt
// Build update params - only include fields that are provided
params := orm.Params{}
fieldMappings := map[string]string{
"supplier_name": "SupplierName",
"supplier_type": "SupplierType",
"supplier_level": "SupplierLevel",
"industry": "Industry",
"contact_person": "ContactPerson",
"contact_phone": "ContactPhone",
"contact_email": "ContactEmail",
"address": "Address",
"register_time": "RegisterTime",
"expire_time": "ExpireTime",
"status": "Status",
"remark": "Remark",
"business_scope": "BusinessScope",
"invoice_title": "InvoiceTitle",
"tax_number": "TaxNumber",
"bank_name": "BankName",
"bank_account": "BankAccount",
"registered_address": "RegisteredAddress",
"registered_phone": "RegisteredPhone",
} }
if err := services.UpdateSupplier(&m); err != nil {
for jsonKey := range fieldMappings {
if value, exists := payload[jsonKey]; exists {
params[jsonKey] = value
}
}
if len(params) == 0 {
c.Data["json"] = map[string]interface{}{"code": 1, "message": "没有要更新的字段"}
c.ServeJSON()
return
}
if err := services.UpdateSupplierFields(idStr, params); err != nil {
c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()} c.Data["json"] = map[string]interface{}{"code": 1, "message": err.Error()}
} else { } else {
c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok"} c.Data["json"] = map[string]interface{}{"code": 0, "message": "ok"}

View File

@ -8,7 +8,7 @@ import (
// Customer 客户模型(对应表 yz_tenant_crm_customer // Customer 客户模型(对应表 yz_tenant_crm_customer
type Customer struct { type Customer struct {
Id string `orm:"pk;size(36)" json:"id"` Id int32 `orm:"pk;auto" json:"id"`
TenantId string `orm:"column(tenant_id);size(64)" json:"tenant_id"` TenantId string `orm:"column(tenant_id);size(64)" json:"tenant_id"`
CustomerName string `orm:"column(customer_name);size(100)" json:"customer_name"` CustomerName string `orm:"column(customer_name);size(100)" json:"customer_name"`
CustomerType string `orm:"column(customer_type);size(20)" json:"customer_type"` CustomerType string `orm:"column(customer_type);size(20)" json:"customer_type"`
@ -22,6 +22,7 @@ type Customer struct {
ExpireTime *time.Time `orm:"column(expire_time);null;type(date)" json:"expire_time"` ExpireTime *time.Time `orm:"column(expire_time);null;type(date)" json:"expire_time"`
Status string `orm:"column(status);size(20)" json:"status"` Status string `orm:"column(status);size(20)" json:"status"`
Remark string `orm:"column(remark);type(text);null" json:"remark"` Remark string `orm:"column(remark);type(text);null" json:"remark"`
BusinessScope string `orm:"column(business_scope);type(longtext);null" json:"business_scope"`
InvoiceTitle string `orm:"column(invoice_title);size(100);null" json:"invoice_title"` InvoiceTitle string `orm:"column(invoice_title);size(100);null" json:"invoice_title"`
TaxNumber string `orm:"column(tax_number);size(50);null" json:"tax_number"` TaxNumber string `orm:"column(tax_number);size(50);null" json:"tax_number"`
BankName string `orm:"column(bank_name);size(100);null" json:"bank_name"` BankName string `orm:"column(bank_name);size(100);null" json:"bank_name"`

View File

@ -8,7 +8,7 @@ import (
// Supplier 供应商模型(对应表 yz_tenant_crm_supplier // Supplier 供应商模型(对应表 yz_tenant_crm_supplier
type Supplier struct { type Supplier struct {
Id int64 `orm:"pk;auto" json:"id"` Id int32 `orm:"pk;auto" json:"id"`
TenantId string `orm:"column(tenant_id);size(64)" json:"tenant_id"` TenantId string `orm:"column(tenant_id);size(64)" json:"tenant_id"`
SupplierName string `orm:"column(supplier_name);size(100)" json:"supplier_name"` SupplierName string `orm:"column(supplier_name);size(100)" json:"supplier_name"`
SupplierType string `orm:"column(supplier_type);size(20)" json:"supplier_type"` SupplierType string `orm:"column(supplier_type);size(20)" json:"supplier_type"`
@ -22,6 +22,7 @@ type Supplier struct {
ExpireTime *time.Time `orm:"column(expire_time);null;type(date)" json:"expire_time"` ExpireTime *time.Time `orm:"column(expire_time);null;type(date)" json:"expire_time"`
Status string `orm:"column(status);size(20)" json:"status"` Status string `orm:"column(status);size(20)" json:"status"`
Remark string `orm:"column(remark);type(text);null" json:"remark"` Remark string `orm:"column(remark);type(text);null" json:"remark"`
BusinessScope string `orm:"column(business_scope);type(longtext);null" json:"business_scope"`
InvoiceTitle string `orm:"column(invoice_title);size(255);null" json:"invoice_title"` InvoiceTitle string `orm:"column(invoice_title);size(255);null" json:"invoice_title"`
TaxNumber string `orm:"column(tax_number);size(20);null" json:"tax_number"` TaxNumber string `orm:"column(tax_number);size(20);null" json:"tax_number"`
BankName string `orm:"column(bank_name);size(50);null" json:"bank_name"` BankName string `orm:"column(bank_name);size(50);null" json:"bank_name"`

View File

@ -396,4 +396,9 @@ func init() {
beego.Router("/api/articles/:id", &controllers.ArticlesController{}, "get:GetArticle;put:UpdateArticle;delete:DeleteArticle") beego.Router("/api/articles/:id", &controllers.ArticlesController{}, "get:GetArticle;put:UpdateArticle;delete:DeleteArticle")
beego.Router("/api/articles/:id/status", &controllers.ArticlesController{}, "patch:UpdateArticleStatus") beego.Router("/api/articles/:id/status", &controllers.ArticlesController{}, "patch:UpdateArticleStatus")
// 文章分类管理路由
beego.Router("/api/categories", &controllers.ArticlesController{}, "get:ListCategories;post:CreateCategory")
beego.Router("/api/categories/:id", &controllers.ArticlesController{}, "get:GetCategory;put:UpdateCategory;delete:DeleteCategory")
beego.Router("/api/categories/:id/status", &controllers.ArticlesController{}, "patch:UpdateCategoryStatus")
} }

View File

@ -1,7 +1,10 @@
package services package services
import ( import (
"fmt"
"server/models" "server/models"
"strconv"
"strings"
"time" "time"
"github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/client/orm"
@ -215,3 +218,274 @@ func GetArticleStats() (map[string]int64, error) {
return stats, nil return stats, nil
} }
// GetArticleCategories 获取文章分类列表
func GetArticleCategories(tenantId int, page, pageSize int, keyword string, status *int8) ([]*models.DictItem, int, error) {
// 首先确保文章分类字典类型存在,如果不存在则创建并添加默认分类
err := ensureDefaultArticleCategories(tenantId)
if err != nil {
return nil, 0, err
}
// 调用字典服务获取文章分类(使用字典编码 "article_category"
// 注意这里传入true表示包含禁用的项目然后我们在业务层进行状态过滤
categories, err := GetDictItemsByCode("article_category", tenantId, true)
if err != nil {
return nil, 0, err
}
// 过滤状态
var filteredCategories []*models.DictItem
if status != nil {
for _, category := range categories {
if category.Status == *status {
filteredCategories = append(filteredCategories, category)
}
}
} else {
filteredCategories = categories
}
// 过滤关键词
if keyword != "" {
var keywordFiltered []*models.DictItem
for _, category := range filteredCategories {
if strings.Contains(category.DictLabel, keyword) {
keywordFiltered = append(keywordFiltered, category)
}
}
filteredCategories = keywordFiltered
}
// 分页处理
total := len(filteredCategories)
start := (page - 1) * pageSize
end := start + pageSize
if start > total {
start = total
}
if end > total {
end = total
}
if start >= end {
return []*models.DictItem{}, total, nil
}
return filteredCategories[start:end], total, nil
}
// GetArticleCategoryById 根据ID获取文章分类详情
func GetArticleCategoryById(id int) (*models.DictItem, error) {
return GetDictItemById(id)
}
// CreateArticleCategory 创建文章分类
func CreateArticleCategory(tenantId, userId int, label, value string, status int8, sort int, color, icon, remark string) (int64, error) {
// 先获取或创建文章分类字典类型
dictType, err := getOrCreateArticleCategoryDictType(tenantId, userId)
if err != nil {
return 0, err
}
// 构建字典项对象
var dictItem models.DictItem
dictItem.DictTypeId = dictType.Id
dictItem.DictLabel = label
dictItem.DictValue = value
dictItem.Status = status
dictItem.Sort = sort
dictItem.Color = color
dictItem.Icon = icon
dictItem.Remark = remark
dictItem.CreateBy = strconv.Itoa(userId)
dictItem.UpdateBy = strconv.Itoa(userId)
// 调用字典服务创建分类
return AddDictItem(&dictItem)
}
// UpdateArticleCategory 更新文章分类
func UpdateArticleCategory(id, userId int, label, value string, status int8, sort int, color, icon, remark string) error {
// 获取现有分类
existingCategory, err := GetDictItemById(id)
if err != nil {
return err
}
if existingCategory == nil {
return fmt.Errorf("分类不存在")
}
// 构建更新对象
var dictItem models.DictItem
dictItem.Id = id
dictItem.DictTypeId = existingCategory.DictTypeId
dictItem.DictLabel = label
dictItem.DictValue = value
dictItem.Status = status
dictItem.Sort = sort
dictItem.Color = color
dictItem.Icon = icon
dictItem.Remark = remark
dictItem.UpdateBy = strconv.Itoa(userId)
// 调用字典服务更新分类
return UpdateDictItem(&dictItem)
}
// DeleteArticleCategory 删除文章分类
func DeleteArticleCategory(id int) error {
// 检查是否有文章使用此分类
o := orm.NewOrm()
var count int
err := o.Raw("SELECT COUNT(*) FROM yz_articles WHERE cate = ? AND delete_time IS NULL", id).QueryRow(&count)
if err == nil && count > 0 {
return fmt.Errorf("该分类下存在文章,无法删除")
}
// 调用字典服务删除分类
return DeleteDictItem(id)
}
// UpdateArticleCategoryStatus 更新文章分类状态
func UpdateArticleCategoryStatus(id int, status int8) error {
// 获取现有分类
category, err := GetDictItemById(id)
if err != nil {
return err
}
if category == nil {
return fmt.Errorf("分类不存在")
}
// 更新状态
category.Status = status
category.UpdateTime = time.Now()
// 调用字典服务更新分类
return UpdateDictItem(category)
}
// ensureDefaultArticleCategories 确保文章分类字典类型和默认分类项存在
func ensureDefaultArticleCategories(tenantId int) error {
o := orm.NewOrm()
// 检查文章分类字典类型是否存在
dictType := &models.DictType{}
err := o.Raw("SELECT * FROM sys_dict_type WHERE dict_code = ? AND tenant_id = ? AND is_deleted = 0",
"article_category", tenantId).QueryRow(dictType)
if err != nil {
// 字典类型不存在,创建它
dictType = &models.DictType{
TenantId: tenantId,
DictCode: "article_category",
DictName: "文章分类",
ParentId: 0,
Status: 1,
IsGlobal: 0,
Sort: 0,
Remark: "文章分类字典",
CreateBy: "system",
UpdateBy: "system",
IsDeleted: 0,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
_, err = o.Insert(dictType)
if err != nil {
return err
}
}
// 检查是否已有分类项,如果没有则创建默认分类
var count int
err = o.Raw("SELECT COUNT(*) FROM sys_dict_item WHERE dict_type_id = ? AND is_deleted = 0", dictType.Id).QueryRow(&count)
if err != nil {
return err
}
if count == 0 {
// 创建默认分类项
defaultCategories := []struct {
label string
value string
sort int
color string
remark string
}{
{"技术分享", "tech", 1, "#409EFF", "技术相关文章"},
{"行业资讯", "news", 2, "#67C23A", "行业新闻资讯"},
{"教程指南", "tutorial", 3, "#E6A23C", "使用教程和指南"},
{"公告通知", "announcement", 4, "#F56C6C", "重要公告通知"},
{"其他", "other", 5, "#909399", "其他类型文章"},
}
for _, cat := range defaultCategories {
dictItem := &models.DictItem{
DictTypeId: dictType.Id,
DictLabel: cat.label,
DictValue: cat.value,
ParentId: 0,
Status: 1,
Sort: cat.sort,
Color: cat.color,
Remark: cat.remark,
CreateBy: "system",
UpdateBy: "system",
IsDeleted: 0,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
_, err = o.Insert(dictItem)
if err != nil {
return err
}
}
}
return nil
}
// getOrCreateArticleCategoryDictType 获取或创建文章分类字典类型
func getOrCreateArticleCategoryDictType(tenantId int, userId int) (*models.DictType, error) {
o := orm.NewOrm()
// 尝试获取现有的文章分类字典类型
dictType := &models.DictType{}
err := o.Raw("SELECT * FROM sys_dict_type WHERE dict_code = ? AND tenant_id = ? AND is_deleted = 0",
"article_category", tenantId).QueryRow(dictType)
if err == nil {
// 已存在,直接返回
return dictType, nil
}
// 不存在,创建新的字典类型
dictType = &models.DictType{
TenantId: tenantId,
DictCode: "article_category",
DictName: "文章分类",
ParentId: 0,
Status: 1,
IsGlobal: 0,
Sort: 0,
Remark: "文章分类字典",
CreateBy: strconv.Itoa(userId),
UpdateBy: strconv.Itoa(userId),
IsDeleted: 0,
CreateTime: time.Now(),
UpdateTime: time.Now(),
}
_, err = o.Insert(dictType)
if err != nil {
return nil, err
}
return dictType, nil
}

View File

@ -1,6 +1,7 @@
package services package services
import ( import (
"strconv"
"time" "time"
"server/models" "server/models"
@ -44,7 +45,11 @@ func ListCustomers(tenantId, keyword, status string, page, pageSize int) (list [
// GetCustomer 通过ID获取客户 // GetCustomer 通过ID获取客户
func GetCustomer(id string) (*models.Customer, error) { func GetCustomer(id string) (*models.Customer, error) {
o := orm.NewOrm() o := orm.NewOrm()
m := models.Customer{Id: id} var customerId int32
if parsedId, err := strconv.ParseInt(id, 10, 64); err == nil {
customerId = int32(parsedId)
}
m := models.Customer{Id: customerId}
if err := o.Read(&m); err != nil { if err := o.Read(&m); err != nil {
return nil, err return nil, err
} }
@ -73,16 +78,38 @@ func UpdateCustomer(m *models.Customer, cols ...string) error {
func SoftDeleteCustomer(id string) error { func SoftDeleteCustomer(id string) error {
o := orm.NewOrm() o := orm.NewOrm()
now := time.Now() now := time.Now()
m := models.Customer{Id: id, DeleteTime: &now} var customerId int32
if parsedId, err := strconv.ParseInt(id, 10, 64); err == nil {
customerId = int32(parsedId)
}
m := models.Customer{Id: customerId, DeleteTime: &now}
_, err := o.Update(&m, "delete_time") _, err := o.Update(&m, "delete_time")
return err return err
} }
// 更新客户字段(支持部分字段更新)
func UpdateCustomerFields(id string, params orm.Params) error {
o := orm.NewOrm()
var customerId int32
if parsedId, err := strconv.ParseInt(id, 10, 64); err == nil {
customerId = int32(parsedId)
}
m := &models.Customer{Id: customerId}
if _, err := o.QueryTable(m).Filter("id", customerId).Update(params); err != nil {
return err
}
return nil
}
// 更新客户开票信息 // 更新客户开票信息
func UpdateInvoice(id string, params orm.Params) error { func UpdateInvoice(id string, params orm.Params) error {
o := orm.NewOrm() o := orm.NewOrm()
m := &models.Customer{Id: id} var customerId int32
if _, err := o.QueryTable(m).Filter("id", id).Update(params); err != nil { if parsedId, err := strconv.ParseInt(id, 10, 64); err == nil {
customerId = int32(parsedId)
}
m := &models.Customer{Id: customerId}
if _, err := o.QueryTable(m).Filter("id", customerId).Update(params); err != nil {
return err return err
} }
return nil return nil

View File

@ -46,7 +46,7 @@ func ListSuppliers(tenantId, keyword, status string, page, pageSize int) (list [
// GetSupplier 通过ID获取供应商 // GetSupplier 通过ID获取供应商
func GetSupplier(id int64) (*models.Supplier, error) { func GetSupplier(id int64) (*models.Supplier, error) {
o := orm.NewOrm() o := orm.NewOrm()
m := models.Supplier{Id: id} m := models.Supplier{Id: int32(id)}
if err := o.Read(&m); err != nil { if err := o.Read(&m); err != nil {
return nil, err return nil, err
} }
@ -75,11 +75,25 @@ func UpdateSupplier(m *models.Supplier, cols ...string) error {
func SoftDeleteSupplier(id int64) error { func SoftDeleteSupplier(id int64) error {
o := orm.NewOrm() o := orm.NewOrm()
now := time.Now() now := time.Now()
m := models.Supplier{Id: id, DeleteTime: &now} m := models.Supplier{Id: int32(id), DeleteTime: &now}
_, err := o.Update(&m, "delete_time") _, err := o.Update(&m, "delete_time")
return err return err
} }
// 更新供应商字段(支持部分字段更新)
func UpdateSupplierFields(id string, params orm.Params) error {
o := orm.NewOrm()
var supplierId int32
if parsedId, err := strconv.ParseInt(id, 10, 64); err == nil {
supplierId = int32(parsedId)
}
m := &models.Supplier{Id: supplierId}
if _, err := o.QueryTable(m).Filter("id", supplierId).Update(params); err != nil {
return err
}
return nil
}
// 更新供应商开票信息 // 更新供应商开票信息
func UpdateSupplierInvoice(params orm.Params) error { func UpdateSupplierInvoice(params orm.Params) error {
o := orm.NewOrm() o := orm.NewOrm()

View File

@ -11,7 +11,8 @@ INSERT INTO `sys_dict_type` (`tenant_id`, `dict_code`, `dict_name`, `parent_id`,
(0, 'user_status', '用户状态', 0, 1, 1, '用户启用禁用状态', 'system'), (0, 'user_status', '用户状态', 0, 1, 1, '用户启用禁用状态', 'system'),
(0, 'user_gender', '用户性别', 0, 1, 2, '用户性别', 'system'), (0, 'user_gender', '用户性别', 0, 1, 2, '用户性别', 'system'),
(0, 'position_status', '职位状态', 0, 1, 3, '职位启用禁用状态', 'system'), (0, 'position_status', '职位状态', 0, 1, 3, '职位启用禁用状态', 'system'),
(0, 'dept_status', '部门状态', 0, 1, 4, '部门启用禁用状态', 'system'); (0, 'dept_status', '部门状态', 0, 1, 4, '部门启用禁用状态', 'system'),
(1, 'article_category', '文章分类', 0, 1, 5, '文章分类字典', 'system');
-- 插入用户状态字典项 -- 插入用户状态字典项
-- 假设 user_status 字典类型 ID 为 1 -- 假设 user_status 字典类型 ID 为 1
@ -37,3 +38,12 @@ INSERT INTO `sys_dict_item` (`dict_type_id`, `dict_label`, `dict_value`, `parent
INSERT INTO `sys_dict_item` (`dict_type_id`, `dict_label`, `dict_value`, `parent_id`, `status`, `sort`, `color`, `remark`, `create_by`) VALUES INSERT INTO `sys_dict_item` (`dict_type_id`, `dict_label`, `dict_value`, `parent_id`, `status`, `sort`, `color`, `remark`, `create_by`) VALUES
(4, '启用', '1', 0, 1, 1, '#67C23A', '部门启用状态', 'system'), (4, '启用', '1', 0, 1, 1, '#67C23A', '部门启用状态', 'system'),
(4, '禁用', '0', 0, 1, 2, '#F56C6C', '部门禁用状态', 'system'); (4, '禁用', '0', 0, 1, 2, '#F56C6C', '部门禁用状态', 'system');
-- 插入文章分类字典项
-- 假设 article_category 字典类型 ID 为 5
INSERT INTO `sys_dict_item` (`dict_type_id`, `dict_label`, `dict_value`, `parent_id`, `status`, `sort`, `color`, `remark`, `create_by`) VALUES
(5, '技术分享', 'tech', 0, 1, 1, '#409EFF', '技术相关文章', 'system'),
(5, '行业资讯', 'news', 0, 1, 2, '#67C23A', '行业新闻资讯', 'system'),
(5, '教程指南', 'tutorial', 0, 1, 3, '#E6A23C', '使用教程和指南', 'system'),
(5, '公告通知', 'announcement', 0, 1, 4, '#F56C6C', '重要公告通知', 'system'),
(5, '其他', 'other', 0, 1, 5, '#909399', '其他类型文章', 'system');