完成新增项目的富文本加密+修改项目的富文本加密及解密+新增项目通知的加密+编辑项目通知的加密与解密+修改项目富文本+修改编辑项目样式
This commit is contained in:
BIN
dist616 - 副本.zip
BIN
dist616 - 副本.zip
Binary file not shown.
BIN
dist616.zip
BIN
dist616.zip
Binary file not shown.
BIN
dist6月9日.zip
BIN
dist6月9日.zip
Binary file not shown.
BIN
greenOrange.zip
BIN
greenOrange.zip
Binary file not shown.
@ -198,6 +198,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, nextTick,ref } from 'vue';
|
import { reactive, onMounted, nextTick,ref } from 'vue';
|
||||||
import RichTextEditor from '../components/RichTextEditor.vue';
|
import RichTextEditor from '../components/RichTextEditor.vue';
|
||||||
@ -220,6 +221,7 @@ interface ProjectForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fileInput = ref<HTMLInputElement | null>(null);
|
const fileInput = ref<HTMLInputElement | null>(null);
|
||||||
|
const fileName = ref('');
|
||||||
const handleFileUpload = async (event: Event) => {
|
const handleFileUpload = async (event: Event) => {
|
||||||
const input = event.target as HTMLInputElement;
|
const input = event.target as HTMLInputElement;
|
||||||
const file = input.files?.[0];
|
const file = input.files?.[0];
|
||||||
@ -227,7 +229,6 @@ const handleFileUpload = async (event: Event) => {
|
|||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 重命名局部变量避免冲突(修复错误❷)
|
|
||||||
const uploadFormData = new FormData();
|
const uploadFormData = new FormData();
|
||||||
uploadFormData.append('biz', 'project');
|
uploadFormData.append('biz', 'project');
|
||||||
uploadFormData.append('file', file);
|
uploadFormData.append('file', file);
|
||||||
@ -241,7 +242,6 @@ const handleFileUpload = async (event: Event) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res.code === 1) {
|
if (res.code === 1) {
|
||||||
// 正确访问响应式对象
|
|
||||||
formData.projectImage = res.data;
|
formData.projectImage = res.data;
|
||||||
fileName.value = file.name;
|
fileName.value = file.name;
|
||||||
}
|
}
|
||||||
@ -251,11 +251,6 @@ const handleFileUpload = async (event: Event) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 添加文件名响应式变量
|
|
||||||
const fileName = ref('');
|
|
||||||
|
|
||||||
// 响应式表单数据
|
// 响应式表单数据
|
||||||
const formData = reactive<ProjectForm>({
|
const formData = reactive<ProjectForm>({
|
||||||
projectName: '',
|
projectName: '',
|
||||||
@ -270,39 +265,90 @@ const formData = reactive<ProjectForm>({
|
|||||||
projectImage: ''
|
projectImage: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Base64编码函数
|
||||||
|
const encode64 = (text: string): string => {
|
||||||
|
return btoa(
|
||||||
|
encodeURIComponent(text).replace(
|
||||||
|
/%([0-9A-F]{2})/g,
|
||||||
|
(_, p1) => String.fromCharCode(parseInt(p1, 16)))
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// 生命周期钩子
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始处理
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
// 为手机预览添加初始样式
|
|
||||||
const phoneScreen = document.querySelector('.phone-screen');
|
const phoneScreen = document.querySelector('.phone-screen');
|
||||||
if (phoneScreen) {
|
if (phoneScreen) {
|
||||||
// 添加阴影和动画效果
|
|
||||||
phoneScreen.classList.add('animate-fade-in');
|
phoneScreen.classList.add('animate-fade-in');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const encryptValue = (value: any, key: string): any => {
|
||||||
|
// 特定字段不加密
|
||||||
|
if (key === 'projectStatus') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数值型字段直接返回,不加密
|
||||||
|
if (key === 'projectSettlementCycle' || key === 'maxPromoterCount' || key === 'projectImage' || key === 'projectName') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 将值转换为字符串
|
||||||
|
const valueString = typeof value === 'object'
|
||||||
|
? JSON.stringify(value)
|
||||||
|
: String(value);
|
||||||
|
|
||||||
|
// 使用Base64编码数据
|
||||||
|
return encode64(valueString);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Base64编码失败:', error);
|
||||||
|
return value; // 编码失败返回原始值
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
|
// 表单验证
|
||||||
|
if (!validateForm()) {
|
||||||
|
alert('请填写所有必填字段');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const storedToken = localStorage.getItem('token');
|
const storedToken = localStorage.getItem('token');
|
||||||
|
|
||||||
const res:any = await myAxios.post(`/project/add`,
|
// 创建加密后的表单对象 - 保持原始数据结构
|
||||||
JSON.stringify(formData),
|
const encryptedFormData: Record<string, any> = {};
|
||||||
{
|
|
||||||
|
console.log("===== 字段Base64编码结果 =====");
|
||||||
|
Object.entries(formData).forEach(([key, value]) => {
|
||||||
|
// 跳过空值字段
|
||||||
|
if (value === null || value === undefined || value === '') return;
|
||||||
|
|
||||||
|
// 对字段值进行Base64编码
|
||||||
|
encryptedFormData[key] = encryptValue(value, key);
|
||||||
|
|
||||||
|
// 打印编码结果
|
||||||
|
console.log(`字段: ${key}`);
|
||||||
|
console.log(`原始值: ${value}`);
|
||||||
|
console.log(`编码值: ${encryptedFormData[key]}`);
|
||||||
|
console.log(`类型: ${typeof encryptedFormData[key]}`);
|
||||||
|
console.log('----------------------');
|
||||||
|
});
|
||||||
|
console.log("=======================");
|
||||||
|
|
||||||
|
// 发送Base64编码后的数据
|
||||||
|
const res: any = await myAxios.post(`/project/add`, encryptedFormData, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': storedToken
|
'Authorization': storedToken
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (res.code === 1) {
|
if (res.code === 1) {
|
||||||
alert('项目创建成功!');
|
alert('项目创建成功!');
|
||||||
// router.back();
|
// 重置表单
|
||||||
router.push('/project')
|
|
||||||
Object.assign(formData, {
|
Object.assign(formData, {
|
||||||
projectName: '',
|
projectName: '',
|
||||||
projectSettlementCycle: 2,
|
projectSettlementCycle: 2,
|
||||||
@ -315,16 +361,40 @@ const handleSubmit = async () => {
|
|||||||
applyPromoCodeDesc: '',
|
applyPromoCodeDesc: '',
|
||||||
projectImage: ''
|
projectImage: ''
|
||||||
});
|
});
|
||||||
|
fileName.value = ''; // 重置文件名
|
||||||
|
router.push('/project');
|
||||||
} else {
|
} else {
|
||||||
alert(`创建失败:${res.message}`);
|
alert(`创建失败:${res.message}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('请求失败:', error);
|
console.error('请求失败:', error);
|
||||||
|
alert('提交失败,请检查控制台获取详细信息');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validateForm = (): boolean => {
|
||||||
|
const requiredFields: (keyof ProjectForm)[] = [
|
||||||
|
'projectName',
|
||||||
|
'projectImage',
|
||||||
|
'projectSettlementCycle',
|
||||||
|
'maxPromoterCount',
|
||||||
|
'projectStatus',
|
||||||
|
'projectDescription',
|
||||||
|
'settlementDesc',
|
||||||
|
'projectDesc',
|
||||||
|
'projectFlow',
|
||||||
|
'applyPromoCodeDesc'
|
||||||
|
];
|
||||||
|
|
||||||
|
return requiredFields.every(field => {
|
||||||
|
const value = formData[field];
|
||||||
|
return value !== '' && value !== null && value !== undefined;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.modern-form {
|
.modern-form {
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
|
@ -86,6 +86,13 @@ import myAxios from "../../api/myAxios.ts";
|
|||||||
import RichTextEditor from '../components/RichTextEditor.vue';
|
import RichTextEditor from '../components/RichTextEditor.vue';
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
|
|
||||||
|
function encode64(text: string): string {
|
||||||
|
// 使用_代替未使用的match参数
|
||||||
|
return btoa(encodeURIComponent(text).replace(/%([0-9A-F]{2})/g, (_, p1) => {
|
||||||
|
return String.fromCharCode(parseInt(p1, 16));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
interface NotificationForm {
|
interface NotificationForm {
|
||||||
notificationTitle: string;
|
notificationTitle: string;
|
||||||
notificationContent: string;
|
notificationContent: string;
|
||||||
@ -110,9 +117,16 @@ onMounted(() => {
|
|||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
const storedToken = localStorage.getItem('token');
|
const storedToken = localStorage.getItem('token');
|
||||||
|
|
||||||
|
// 创建要发送的数据副本
|
||||||
|
const payload = {
|
||||||
|
...formData,
|
||||||
|
notificationContent: encode64(formData.notificationContent)
|
||||||
|
};
|
||||||
|
|
||||||
const res: any = await myAxios.post(
|
const res: any = await myAxios.post(
|
||||||
'/projectNotification/add',
|
'/projectNotification/add',
|
||||||
JSON.stringify(formData),
|
JSON.stringify(payload),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -124,11 +138,10 @@ const handleSubmit = async () => {
|
|||||||
if (res.code === 1) {
|
if (res.code === 1) {
|
||||||
alert('通知发布成功!');
|
alert('通知发布成功!');
|
||||||
router.back();
|
router.back();
|
||||||
Object.assign(formData, {
|
// 重置表单
|
||||||
notificationTitle: '',
|
formData.notificationTitle = '';
|
||||||
notificationContent: '',
|
formData.notificationContent = '';
|
||||||
projectId: ''
|
formData.projectId = '';
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
alert(`发布失败:${res.message}`);
|
alert(`发布失败:${res.message}`);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,24 @@ import myAxios from "../../api/myAxios.ts";
|
|||||||
import { NotificationOutlined } from '@ant-design/icons-vue';
|
import { NotificationOutlined } from '@ant-design/icons-vue';
|
||||||
import RichTextEditor from '../components/RichTextEditor.vue'; // 引入富文本编辑器
|
import RichTextEditor from '../components/RichTextEditor.vue'; // 引入富文本编辑器
|
||||||
import '@vueup/vue-quill/dist/vue-quill.snow.css'; // 引入富文本编辑器样式
|
import '@vueup/vue-quill/dist/vue-quill.snow.css'; // 引入富文本编辑器样式
|
||||||
|
function encode64(text: string): string {
|
||||||
|
return btoa(encodeURIComponent(text).replace(/%([0-9A-F]{2})/g, (_, p1) => {
|
||||||
|
return String.fromCharCode(parseInt(p1, 16));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode64(text: string): string {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(
|
||||||
|
Array.from(atob(text), char =>
|
||||||
|
'%' + ('00' + char.charCodeAt(0).toString(16)).slice(-2)
|
||||||
|
).join('')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Base64解码失败:', error);
|
||||||
|
return text; // 解码失败时返回原始文本
|
||||||
|
}
|
||||||
|
}
|
||||||
interface NotificationDetail {
|
interface NotificationDetail {
|
||||||
id: string; // 改为 string 类型
|
id: string; // 改为 string 类型
|
||||||
notificationTitle: string;
|
notificationTitle: string;
|
||||||
@ -94,44 +111,45 @@ const cancelEdit = () => {
|
|||||||
isEditing.value = false;
|
isEditing.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 保存编辑 - 根据接口要求修改
|
|
||||||
// 修改保存函数
|
|
||||||
const saveNotice = async () => {
|
const saveNotice = async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const storedToken = localStorage.getItem('token');
|
const storedToken = localStorage.getItem('token');
|
||||||
|
|
||||||
// 创建符合接口要求的JSON对象
|
// 对通知内容进行Base64编码
|
||||||
|
const encodedContent = encode64(editableNotice.value.notificationContent || '');
|
||||||
|
|
||||||
|
// 创建请求数据
|
||||||
const requestData = {
|
const requestData = {
|
||||||
id: editableNotice.value.id,
|
id: editableNotice.value.id,
|
||||||
notificationTitle: editableNotice.value.notificationTitle,
|
notificationTitle: editableNotice.value.notificationTitle,
|
||||||
notificationContent: editableNotice.value.notificationContent,
|
notificationContent: encodedContent, // 使用编码后的内容
|
||||||
projectId: editableNotice.value.projectId
|
projectId: editableNotice.value.projectId
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送JSON格式的数据
|
|
||||||
const res:any = await myAxios.post(
|
const res:any = await myAxios.post(
|
||||||
"/projectNotification/update",
|
"/projectNotification/update",
|
||||||
requestData, // 直接发送JSON对象
|
requestData,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: storedToken,
|
Authorization: storedToken,
|
||||||
'Content-Type': 'application/json' // 关键修改:使用JSON格式
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.code === 1) {
|
if (res.code === 1) {
|
||||||
message.success('更新成功');
|
message.success('更新成功');
|
||||||
// 更新本地数据并退出编辑模式
|
// 更新本地数据并退出编辑模式 - 使用解码后的内容
|
||||||
projectNotice.value = { ...editableNotice.value };
|
projectNotice.value = {
|
||||||
|
...editableNotice.value, // 这里包含的是用户编辑的原始内容(未编码)
|
||||||
|
};
|
||||||
isEditing.value = false;
|
isEditing.value = false;
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message || '更新失败');
|
message.error(res.message || '更新失败');
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('更新通知失败:', error);
|
console.error('更新通知失败:', error);
|
||||||
// 改进错误处理
|
|
||||||
const errMsg = error.response?.data?.message ||
|
const errMsg = error.response?.data?.message ||
|
||||||
error.message ||
|
error.message ||
|
||||||
'更新通知失败,请检查网络';
|
'更新通知失败,请检查网络';
|
||||||
@ -140,9 +158,6 @@ const saveNotice = async () => {
|
|||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const fetchNotificationDetail = async (id: number) => {
|
const fetchNotificationDetail = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@ -161,10 +176,13 @@ const fetchNotificationDetail = async (id: number) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (res.code === 1 && res.data) {
|
if (res.code === 1 && res.data) {
|
||||||
|
// 对通知内容进行Base64解码
|
||||||
|
const decodedContent = decode64(res.data.notificationContent);
|
||||||
|
|
||||||
projectNotice.value = {
|
projectNotice.value = {
|
||||||
id: res.data.id,
|
id: res.data.id,
|
||||||
notificationTitle: res.data.notificationTitle,
|
notificationTitle: res.data.notificationTitle,
|
||||||
notificationContent: res.data.notificationContent,
|
notificationContent: decodedContent,
|
||||||
projectId: res.data.projectId,
|
projectId: res.data.projectId,
|
||||||
createTime: res.data.createTime
|
createTime: res.data.createTime
|
||||||
};
|
};
|
||||||
@ -183,8 +201,6 @@ const fetchNotificationDetail = async (id: number) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
router.back();
|
router.back();
|
||||||
};
|
};
|
||||||
|
@ -110,10 +110,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -3,7 +3,7 @@ import {useRoute, useRouter} from "vue-router";
|
|||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import myAxios from "../../api/myAxios";
|
import myAxios from "../../api/myAxios";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import { QuillEditor } from '@vueup/vue-quill';
|
import RichTextEditor from '../components/RichTextEditor.vue';
|
||||||
import {Form} from 'ant-design-vue'
|
import {Form} from 'ant-design-vue'
|
||||||
import {downLoadImage} from "../../api/ImageUrl.ts";
|
import {downLoadImage} from "../../api/ImageUrl.ts";
|
||||||
|
|
||||||
@ -36,10 +36,53 @@ const projectData = ref<ProjectDetail>({
|
|||||||
projectFlow: '',
|
projectFlow: '',
|
||||||
applyPromoCodeDesc: ''
|
applyPromoCodeDesc: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const editData = ref<ProjectDetail | null>(null);
|
||||||
|
|
||||||
const projectId = ref<string | null>(null);
|
const projectId = ref<string | null>(null);
|
||||||
const isEditing = ref(false);
|
const isEditing = ref(false);
|
||||||
const fileInput = ref<HTMLInputElement | null>(null);
|
const fileInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
function encode64(text: string): string {
|
||||||
|
return btoa(encodeURIComponent(text).replace(/%([0-9A-F]{2})/g, (_, p1) => {
|
||||||
|
return String.fromCharCode(parseInt(p1, 16));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode64(text: string): string {
|
||||||
|
return decodeURIComponent(
|
||||||
|
Array.from(atob(text), char =>
|
||||||
|
'%' + ('00' + char.charCodeAt(0).toString(16)).slice(-2)
|
||||||
|
).join('')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptProjectData = (data: ProjectDetail): ProjectDetail => {
|
||||||
|
const decryptedData: ProjectDetail = { ...data };
|
||||||
|
|
||||||
|
const fieldsToDecrypt: (keyof ProjectDetail)[] = [
|
||||||
|
'projectName',
|
||||||
|
'projectDescription',
|
||||||
|
'settlementDesc',
|
||||||
|
'projectDesc',
|
||||||
|
'projectFlow',
|
||||||
|
'applyPromoCodeDesc'
|
||||||
|
];
|
||||||
|
|
||||||
|
fieldsToDecrypt.forEach(field => {
|
||||||
|
const value = decryptedData[field];
|
||||||
|
if (typeof value === 'string' && value) {
|
||||||
|
try {
|
||||||
|
(decryptedData as Record<keyof ProjectDetail, any>)[field] = decode64(value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Base64解码失败 (${field}):`, error);
|
||||||
|
(decryptedData as Record<keyof ProjectDetail, any>)[field] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return decryptedData;
|
||||||
|
};
|
||||||
|
|
||||||
// 图片上传处理逻辑
|
// 图片上传处理逻辑
|
||||||
const handleFileUpload = async (event: Event) => {
|
const handleFileUpload = async (event: Event) => {
|
||||||
@ -52,7 +95,6 @@ const handleFileUpload = async (event: Event) => {
|
|||||||
// 本地预览
|
// 本地预览
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
// 仅设置临时预览,不覆盖实际值
|
|
||||||
previewImage.value = e.target?.result as string;
|
previewImage.value = e.target?.result as string;
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
@ -71,8 +113,9 @@ const handleFileUpload = async (event: Event) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res.code === 1 && res.data) {
|
if (res.code === 1 && res.data) {
|
||||||
// 核心修改:将返回的文件标识符赋值给projectImage
|
if (editData.value) {
|
||||||
projectData.value.projectImage = res.data;
|
editData.value.projectImage = res.data;
|
||||||
|
}
|
||||||
message.success('图片上传成功');
|
message.success('图片上传成功');
|
||||||
} else {
|
} else {
|
||||||
throw new Error(res.message || '上传失败');
|
throw new Error(res.message || '上传失败');
|
||||||
@ -81,12 +124,15 @@ const handleFileUpload = async (event: Event) => {
|
|||||||
console.error('上传失败:', error);
|
console.error('上传失败:', error);
|
||||||
message.error('文件上传失败');
|
message.error('文件上传失败');
|
||||||
previewImage.value = '';
|
previewImage.value = '';
|
||||||
|
} finally {
|
||||||
|
// 重置文件输入,允许重复上传同一文件
|
||||||
|
if (input) input.value = '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const previewImage = ref('');
|
const previewImage = ref('');
|
||||||
|
|
||||||
const formRef = ref<typeof Form>(); // 添加表单引用
|
const formRef = ref<typeof Form>();
|
||||||
|
|
||||||
// 添加数字输入校验方法
|
// 添加数字输入校验方法
|
||||||
const handleNumberInput = (e: KeyboardEvent) => {
|
const handleNumberInput = (e: KeyboardEvent) => {
|
||||||
@ -105,24 +151,38 @@ const handlePaste = (e: ClipboardEvent) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新项目时提交处理
|
|
||||||
const finishEditing = async () => {
|
const finishEditing = async () => {
|
||||||
//isEditing.value = false;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!editData.value) {
|
||||||
|
message.error('编辑数据不存在');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const storedToken = localStorage.getItem('token');
|
const storedToken = localStorage.getItem('token');
|
||||||
if (!storedToken) throw new Error('未找到登录信息');
|
if (!storedToken) {
|
||||||
|
message.error('未找到登录信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 转换富文本内容为字符串
|
const payload: Record<string, any> = { ...editData.value };
|
||||||
const payload = {
|
|
||||||
...projectData.value,
|
|
||||||
settlementDesc: String(projectData.value.settlementDesc),
|
|
||||||
projectDesc: String(projectData.value.projectDesc),
|
|
||||||
projectFlow: String(projectData.value.projectFlow),
|
|
||||||
applyPromoCodeDesc: String(projectData.value.applyPromoCodeDesc)
|
|
||||||
};
|
|
||||||
|
|
||||||
const res:any = await myAxios.post("/project/update",
|
const fieldsToEncode: (keyof ProjectDetail)[] = [
|
||||||
|
'projectDescription',
|
||||||
|
'settlementDesc',
|
||||||
|
'projectDesc',
|
||||||
|
'projectFlow',
|
||||||
|
'applyPromoCodeDesc'
|
||||||
|
];
|
||||||
|
|
||||||
|
fieldsToEncode.forEach(field => {
|
||||||
|
const value = payload[field];
|
||||||
|
if (typeof value === 'string' && value) {
|
||||||
|
payload[field] = encode64(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const res: any = await myAxios.post(
|
||||||
|
"/project/update",
|
||||||
JSON.stringify(payload),
|
JSON.stringify(payload),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@ -131,25 +191,24 @@ const finishEditing = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log(res)
|
|
||||||
if (res.code === 1) {
|
if (res.code === 1) {
|
||||||
message.success('项目更新成功');
|
message.success('项目更新成功');
|
||||||
|
projectData.value = editData.value;
|
||||||
|
editData.value = null;
|
||||||
router.push('/project')
|
router.push('/project')
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message);
|
message.error(res.message);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("更新失败:", error);
|
console.error("更新失败:", error);
|
||||||
|
|
||||||
message.error('项目更新失败');
|
message.error('项目更新失败');
|
||||||
return;
|
} finally {
|
||||||
|
isEditing.value = false;
|
||||||
|
previewImage.value = ''; // 清空预览图
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (typeof route.query.id === 'string') {
|
if (typeof route.query.id === 'string') {
|
||||||
projectId.value = route.query.id;
|
projectId.value = route.query.id;
|
||||||
}
|
}
|
||||||
@ -160,24 +219,34 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const handleIdSearch = async (id: string) => {
|
const handleIdSearch = async (id: string) => {
|
||||||
if (!id) return message.warning("请输入项目ID");
|
if (!id) {
|
||||||
if (!/^\d+$/.test(id)) return message.warning("ID必须为数字");
|
message.warning("请输入项目ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^\d+$/.test(id)) {
|
||||||
|
message.warning("ID必须为数字");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const storedToken = localStorage.getItem('token');
|
const storedToken = localStorage.getItem('token');
|
||||||
if (!storedToken) throw new Error('未找到登录信息');
|
if (!storedToken) {
|
||||||
|
message.error('未找到登录信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const res:any = await myAxios.post("/project/queryById",
|
const res: any = await myAxios.post(
|
||||||
|
"/project/queryById",
|
||||||
{ id: parseInt(id) },
|
{ id: parseInt(id) },
|
||||||
{ headers: { Authorization: storedToken } }
|
{ headers: { Authorization: storedToken } }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.code === 1 && res.data) {
|
if (res.code === 1 && res.data) {
|
||||||
console.log('Received project data:', res.data);
|
const decryptedData = decryptProjectData(res.data);
|
||||||
projectData.value = res.data;
|
projectData.value = decryptedData;
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message || '查询失败');
|
message.error(res.message || '查询失败');
|
||||||
}
|
}
|
||||||
@ -194,6 +263,9 @@ const updateProject = () => {
|
|||||||
message.warning('项目数据尚未加载完成,请稍后再试');
|
message.warning('项目数据尚未加载完成,请稍后再试');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 创建编辑数据的深拷贝副本
|
||||||
|
editData.value = JSON.parse(JSON.stringify(projectData.value));
|
||||||
|
previewImage.value = ''; // 进入编辑模式时重置预览图
|
||||||
isEditing.value = true;
|
isEditing.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -202,12 +274,17 @@ const router = useRouter();
|
|||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
router.push('/project')
|
router.push('/project')
|
||||||
};
|
};
|
||||||
</script>
|
|
||||||
|
|
||||||
|
// 触发文件选择
|
||||||
|
const triggerFileInput = () => {
|
||||||
|
if (fileInput.value) {
|
||||||
|
fileInput.value.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="project-detail-container">
|
<div class="project-detail-container">
|
||||||
|
|
||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
<div v-if="loading" class="loading">加载中...</div>
|
<div v-if="loading" class="loading">加载中...</div>
|
||||||
|
|
||||||
@ -265,9 +342,9 @@ const goBack = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ✅ 编辑页 - 使用 addProject.vue 结构 -->
|
<!-- 编辑页 -->
|
||||||
<div v-if="isEditing && projectData" class="add-project-container">
|
<div v-if="isEditing && editData" class="add-project-container">
|
||||||
<a-form ref="formRef" :model="projectData" layout="vertical" @submit.prevent="finishEditing">
|
<a-form ref="formRef" :model="editData" layout="vertical" @submit.prevent="finishEditing">
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<div class="form-header">
|
<div class="form-header">
|
||||||
<h2>项目基本信息</h2>
|
<h2>项目基本信息</h2>
|
||||||
@ -276,12 +353,12 @@ const goBack = () => {
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="项目名称">
|
<a-form-item label="项目名称">
|
||||||
<a-input v-model:value="projectData.projectName" placeholder="请输入项目名称" />
|
<a-input v-model:value="editData.projectName" placeholder="请输入项目名称" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item label="项目状态">
|
<a-form-item label="项目状态">
|
||||||
<a-select v-model:value="projectData.projectStatus" placeholder="请选择状态">
|
<a-select v-model:value="editData.projectStatus" placeholder="请选择状态">
|
||||||
<a-select-option value="running">运行中</a-select-option>
|
<a-select-option value="running">运行中</a-select-option>
|
||||||
<a-select-option value="full">人数已满</a-select-option>
|
<a-select-option value="full">人数已满</a-select-option>
|
||||||
<a-select-option value="paused">已暂停</a-select-option>
|
<a-select-option value="paused">已暂停</a-select-option>
|
||||||
@ -303,7 +380,7 @@ const goBack = () => {
|
|||||||
}]"
|
}]"
|
||||||
>
|
>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
v-model:value="projectData.projectSettlementCycle"
|
v-model:value="editData.projectSettlementCycle"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:min="0"
|
:min="0"
|
||||||
@keypress="handleNumberInput"
|
@keypress="handleNumberInput"
|
||||||
@ -323,7 +400,7 @@ const goBack = () => {
|
|||||||
}]"
|
}]"
|
||||||
>
|
>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
v-model:value="projectData.maxPromoterCount"
|
v-model:value="editData.maxPromoterCount"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:min="1"
|
:min="1"
|
||||||
@keypress="handleNumberInput"
|
@keypress="handleNumberInput"
|
||||||
@ -342,12 +419,14 @@ const goBack = () => {
|
|||||||
@change="handleFileUpload"
|
@change="handleFileUpload"
|
||||||
ref="fileInput"
|
ref="fileInput"
|
||||||
>
|
>
|
||||||
<div class="upload-button" @click="fileInput?.click()">
|
<div class="upload-area" @click="triggerFileInput">
|
||||||
<span v-if="!projectData.projectImage && !previewImage">点击上传图片</span>
|
<div class="upload-button">
|
||||||
<span v-else class="file-name">已选择图片</span>
|
<span>点击上传图片</span>
|
||||||
|
<div class="file-name">支持 JPG/PNG 格式,大小不超过 5MB</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 修改:优先显示预览图,没有预览图时显示服务器图片 -->
|
</div>
|
||||||
<div v-if="previewImage || projectData.projectImage" class="preview-image">
|
|
||||||
|
<div v-if="previewImage || editData.projectImage" class="preview-image">
|
||||||
<img
|
<img
|
||||||
v-if="previewImage"
|
v-if="previewImage"
|
||||||
:src="previewImage"
|
:src="previewImage"
|
||||||
@ -355,7 +434,7 @@ const goBack = () => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-else
|
v-else
|
||||||
:src="downLoadImage + projectData.projectImage"
|
:src="downLoadImage + editData.projectImage"
|
||||||
alt="项目封面预览"
|
alt="项目封面预览"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -363,28 +442,60 @@ const goBack = () => {
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="项目描述">
|
<a-form-item label="项目描述">
|
||||||
<a-textarea v-model:value="projectData.projectDescription" :rows="4" />
|
<textarea
|
||||||
|
v-model="editData.projectDescription"
|
||||||
|
class="textarea-field"
|
||||||
|
placeholder="请输入项目简介..."
|
||||||
|
maxlength="30"
|
||||||
|
></textarea>
|
||||||
|
<div class="absolute right-2 bottom-2 text-xs text-gray-500">
|
||||||
|
{{ editData.projectDescription.length }}/30
|
||||||
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<h2>富文本配置</h2>
|
<h2>富文本配置</h2>
|
||||||
|
|
||||||
<a-form-item label="结算说明">
|
<div class="rich-text-container">
|
||||||
<QuillEditor v-model:content="projectData.settlementDesc" content-type="html" />
|
<div class="rich-text-columns">
|
||||||
</a-form-item>
|
<div class="rich-text-group">
|
||||||
|
<span class="label-text">结算说明</span>
|
||||||
|
<RichTextEditor
|
||||||
|
v-model="editData.settlementDesc"
|
||||||
|
:disable="false"
|
||||||
|
@content-change="(html: string) => { if (editData) editData.settlementDesc = html }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-form-item label="项目说明">
|
<div class="rich-text-group">
|
||||||
<QuillEditor v-model:content="projectData.projectDesc" content-type="html" />
|
<span class="label-text">项目说明</span>
|
||||||
</a-form-item>
|
<RichTextEditor
|
||||||
|
v-model="editData.projectDesc"
|
||||||
|
:disable="false"
|
||||||
|
@content-change="(html: string) => { if (editData) editData.projectDesc = html }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-form-item label="项目流程">
|
<div class="rich-text-group">
|
||||||
<QuillEditor v-model:content="projectData.projectFlow" content-type="html" />
|
<span class="label-text">项目流程</span>
|
||||||
</a-form-item>
|
<RichTextEditor
|
||||||
|
v-model="editData.projectFlow"
|
||||||
|
:disable="false"
|
||||||
|
@content-change="(html: string) => { if (editData) editData.projectFlow = html }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-form-item label="申请推广码说明">
|
<div class="rich-text-group">
|
||||||
<QuillEditor v-model:content="projectData.applyPromoCodeDesc" content-type="html" />
|
<span class="label-text">申请推广码说明</span>
|
||||||
</a-form-item>
|
<RichTextEditor
|
||||||
|
v-model="editData.applyPromoCodeDesc"
|
||||||
|
:disable="false"
|
||||||
|
@content-change="(html: string) => { if (editData) editData.applyPromoCodeDesc = html }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
@ -403,10 +514,8 @@ const goBack = () => {
|
|||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 原有样式保持不变 */
|
|
||||||
.project-detail-container {
|
.project-detail-container {
|
||||||
max-width: 1200px;
|
width: 100%;
|
||||||
margin: 2rem auto;
|
margin: 2rem auto;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
@ -436,13 +545,7 @@ const goBack = () => {
|
|||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
flex-grow: 1;
|
||||||
|
|
||||||
.status-tag {
|
|
||||||
padding: 4px 12px;
|
|
||||||
border-radius: 16px;
|
|
||||||
color: white;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.basic-info {
|
.basic-info {
|
||||||
@ -540,31 +643,16 @@ const goBack = () => {
|
|||||||
.project-title {
|
.project-title {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-section {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input,
|
|
||||||
.form-group textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.6rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 6px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group textarea {
|
|
||||||
height: 100px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-project-container {
|
.add-project-container {
|
||||||
max-width: 960px;
|
width: 100%;
|
||||||
margin: 2rem auto;
|
margin: 2rem auto;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -586,25 +674,16 @@ const goBack = () => {
|
|||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ql-container {
|
.ql-container {
|
||||||
min-height: 120px;
|
min-height: 120px;
|
||||||
}
|
}
|
||||||
.preview-image {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-image img {
|
|
||||||
max-width: 200px;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 保持原有上传样式 */
|
|
||||||
.file-upload {
|
.file-upload {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-input {
|
.file-input {
|
||||||
@ -614,16 +693,22 @@ const goBack = () => {
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload-area {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.upload-button {
|
.upload-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 0.8rem 1.5rem;
|
padding: 1.5rem;
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
border: 2px dashed #cbd5e1;
|
border: 2px dashed #cbd5e1;
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.upload-button:hover {
|
.upload-button:hover {
|
||||||
@ -631,23 +716,41 @@ const goBack = () => {
|
|||||||
border-color: #94a3b8;
|
border-color: #94a3b8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-name {
|
.upload-button span {
|
||||||
color: #64748b;
|
font-weight: 500;
|
||||||
font-size: 0.9rem;
|
color: #334155;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*橙色按钮*/
|
.file-name {
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
margin-top: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 300px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
.custom-button {
|
.custom-button {
|
||||||
background-color: #ffa940;
|
background-color: #ffa940;
|
||||||
border-color: #ffa940;
|
border-color: #ffa940;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-button:hover,
|
.custom-button:hover,
|
||||||
.custom-button:focus {
|
.custom-button:focus {
|
||||||
background-color: #ffa940;
|
background-color: #fa8c16;
|
||||||
border-color: #ffa940;
|
border-color: #fa8c16;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,7 +761,6 @@ const goBack = () => {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 危险按钮样式 */
|
|
||||||
.custom-button.ant-btn-dangerous {
|
.custom-button.ant-btn-dangerous {
|
||||||
background-color: #ff4d4f;
|
background-color: #ff4d4f;
|
||||||
border-color: #ff4d4f;
|
border-color: #ff4d4f;
|
||||||
@ -670,23 +772,89 @@ const goBack = () => {
|
|||||||
border-color: #ff7875;
|
border-color: #ff7875;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rich-text-container {
|
||||||
|
margin-top: 1.5rem;
|
||||||
.custom-search :deep(.ant-input-search-button) {
|
|
||||||
background-color: #ffa940;
|
|
||||||
border-color: #ffa940;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-search :deep(.ant-input-search-button:hover),
|
.rich-text-columns {
|
||||||
.custom-search :deep(.ant-input-search-button:focus) {
|
display: grid;
|
||||||
background-color: #fa8c16;
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
border-color: #fa8c16;
|
gap: 1rem;
|
||||||
|
align-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 保持输入框原有样式不变 */
|
.rich-text-group {
|
||||||
.custom-search :deep(.ant-input) {
|
display: flex;
|
||||||
border-right-color: #ffa940;
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
height: 100%;
|
||||||
|
border: 2px solid #e2e8f0;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
min-height: 320px;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 10px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-group:focus-within {
|
||||||
|
border-color: #6366f1;
|
||||||
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-text {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #3b4151;
|
||||||
|
padding-left: 0.2rem;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-group:focus-within .label-text {
|
||||||
|
color: #6366f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1280px) {
|
||||||
|
.rich-text-columns {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.rich-text-columns {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ql-container) {
|
||||||
|
height: 280px;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea-field {
|
||||||
|
min-height: 100px;
|
||||||
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.textarea-field:focus {
|
||||||
|
border-color: #6366f1;
|
||||||
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.textarea-field {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.8rem 1.2rem;
|
||||||
|
border: 2px solid #e2e8f0;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-size: 1rem;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -4,6 +4,19 @@ import {useRoute, useRouter} from "vue-router";
|
|||||||
import myAxios from "../../api/myAxios";
|
import myAxios from "../../api/myAxios";
|
||||||
import {message} from "ant-design-vue";
|
import {message} from "ant-design-vue";
|
||||||
|
|
||||||
|
function decode64(text: string): string {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(
|
||||||
|
Array.from(atob(text), char =>
|
||||||
|
'%' + ('00' + char.charCodeAt(0).toString(16)).slice(-2)
|
||||||
|
).join('')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Base64解码失败:', error);
|
||||||
|
return text; // 解码失败时返回原始文本
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '项目通知ID',
|
title: '项目通知ID',
|
||||||
@ -112,6 +125,7 @@ onMounted(() => {
|
|||||||
getNotifications(projectId.value);
|
getNotifications(projectId.value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const getNotifications = async (id: string | number) => {
|
const getNotifications = async (id: string | number) => {
|
||||||
const storedToken = localStorage.getItem('token');
|
const storedToken = localStorage.getItem('token');
|
||||||
try {
|
try {
|
||||||
@ -126,10 +140,13 @@ const getNotifications = async (id: string | number) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log(response)
|
|
||||||
if (response.code === 1) {
|
if (response.code === 1) {
|
||||||
originalTableData.value = response.data;
|
// 对通知内容进行Base64解码
|
||||||
displayData.value = response.data; // 初始显示所有数据
|
originalTableData.value = response.data.map((item:any) => ({
|
||||||
|
...item,
|
||||||
|
notificationContent: decode64(item.notificationContent)
|
||||||
|
}));
|
||||||
|
displayData.value = originalTableData.value;
|
||||||
} else {
|
} else {
|
||||||
error.value = "获取通知列表失败";
|
error.value = "获取通知列表失败";
|
||||||
originalTableData.value = [];
|
originalTableData.value = [];
|
||||||
@ -144,6 +161,7 @@ const getNotifications = async (id: string | number) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (projectId.value) {
|
if (projectId.value) {
|
||||||
getNotifications(projectId.value);
|
getNotifications(projectId.value);
|
||||||
@ -232,6 +250,7 @@ const goBack = () => {
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-button class="custom-button" @click="goAddProjectNotice">新增项目通知</a-button>
|
<a-button class="custom-button" @click="goAddProjectNotice">新增项目通知</a-button>
|
||||||
<a-button class="custom-button" @click="reset">重置搜索</a-button>
|
<a-button class="custom-button" @click="reset">重置搜索</a-button>
|
||||||
|
<a-button @click="goBack" class="custom-button">返回</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
@ -256,11 +275,6 @@ const goBack = () => {
|
|||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
|
||||||
<div class="table-footer">
|
|
||||||
<a-button @click="goBack" class="back-button">返回</a-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
</a-table>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
@ -274,29 +288,6 @@ const goBack = () => {
|
|||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-alert {
|
|
||||||
padding: 1rem;
|
|
||||||
background: #ffe3e3;
|
|
||||||
color: #ff4444;
|
|
||||||
border-radius: 6px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.8rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-icon {
|
|
||||||
display: inline-block;
|
|
||||||
width: 1.2rem;
|
|
||||||
height: 1.2rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #ff4444;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 1.2rem;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table-thead) {
|
:deep(.ant-table-thead) {
|
||||||
background-color: #fafafa !important;
|
background-color: #fafafa !important;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -362,17 +353,6 @@ const goBack = () => {
|
|||||||
border-right-color: #ffa940;
|
border-right-color: #ffa940;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 新增表格页脚样式 */
|
|
||||||
.table-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-button {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 调整分页器位置 */
|
/* 调整分页器位置 */
|
||||||
:deep(.ant-table-pagination.ant-pagination) {
|
:deep(.ant-table-pagination.ant-pagination) {
|
||||||
|
@ -1 +1 @@
|
|||||||
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/imageurl.ts","./src/api/myaxios.ts","./src/router/index.ts","./src/router/routes.ts","./src/store/index.ts","./src/store/userstore.ts","./src/types/wangeditor.d.ts","./src/app.vue","./src/layout/managelayout.vue","./src/layout/manage/manageheader.vue","./src/layout/manage/managesidebar.vue","./src/view/index.vue","./src/view/login.vue","./src/view/test.vue","./src/view/community/community.vue","./src/view/components/richtexteditor.vue","./src/view/course/linkedcourse.vue","./src/view/course/localcurriculum.vue","./src/view/project/addproject.vue","./src/view/project/addprojectnotice.vue","./src/view/project/moneydetail.vue","./src/view/project/noticedetail.vue","./src/view/project/project.vue","./src/view/project/projectdetail.vue","./src/view/project/projectnotice.vue","./src/view/project/promotioncode.vue","./src/view/settlement/applicationrecord.vue","./src/view/settlement/moneyrecord.vue","./src/view/userlist/userlist.vue","./src/view/work/workdetail.vue","./src/view/work/worklist.vue"],"version":"5.6.3"}
|
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/imageurl.ts","./src/api/myaxios.ts","./src/router/index.ts","./src/router/routes.ts","./src/store/index.ts","./src/store/userstore.ts","./src/types/wangeditor.d.ts","./src/app.vue","./src/layout/managelayout.vue","./src/layout/manage/manageheader.vue","./src/layout/manage/managesidebar.vue","./src/view/index.vue","./src/view/login.vue","./src/view/test.vue","./src/view/community/community.vue","./src/view/components/richtexteditor.vue","./src/view/course/linkedcourse.vue","./src/view/course/localcurriculum.vue","./src/view/project/addproject.vue","./src/view/project/addprojectnotice.vue","./src/view/project/moneydetail.vue","./src/view/project/noticedetail.vue","./src/view/project/project.vue","./src/view/project/projectdetail.vue","./src/view/project/projectnotice.vue","./src/view/project/promotioncode.vue","./src/view/settlement/applicationrecord.vue","./src/view/settlement/moneyrecord.vue","./src/view/settlement/withdrawalapplicationrecord.vue","./src/view/userlist/userlist.vue","./src/view/work/workdetail.vue","./src/view/work/worklist.vue"],"version":"5.6.3"}
|
Reference in New Issue
Block a user