You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
6.2 KiB
247 lines
6.2 KiB
<template>
|
|
<div class="float-ball-setting">
|
|
<div class="setting-header">
|
|
<el-button type="primary" @click="showSettingDialog = true">
|
|
<el-icon><Setting /></el-icon>
|
|
悬浮球设置
|
|
</el-button>
|
|
</div>
|
|
|
|
<!-- 悬浮球设置弹窗 -->
|
|
<el-dialog
|
|
v-model="showSettingDialog"
|
|
title="悬浮球图片设置"
|
|
width="500px"
|
|
:close-on-click-modal="false"
|
|
>
|
|
<el-form :model="floatBallConfig" label-width="120px">
|
|
<el-form-item label="PC端悬浮球">
|
|
<div class="upload-section">
|
|
<image-upload
|
|
v-model="floatBallConfig.pcImage"
|
|
v-model:thumb="floatBallConfig.pcImageThumb"
|
|
:width="80"
|
|
:height="80"
|
|
/>
|
|
<div class="upload-tip">
|
|
<p>建议尺寸:60x60px</p>
|
|
</div>
|
|
</div>
|
|
</el-form-item>
|
|
|
|
<el-form-item label="移动端悬浮球">
|
|
<div class="upload-section">
|
|
<image-upload
|
|
v-model="floatBallConfig.mobileImage"
|
|
v-model:thumb="floatBallConfig.mobileImageThumb"
|
|
:width="80"
|
|
:height="80"
|
|
/>
|
|
<div class="upload-tip">
|
|
<p>建议尺寸:50x50px</p>
|
|
</div>
|
|
</div>
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="showSettingDialog = false">取消</el-button>
|
|
<el-button type="primary" @click="saveFloatBallConfig">
|
|
保存设置
|
|
</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted, watch } from 'vue';
|
|
import { Setting } from '@element-plus/icons-vue';
|
|
import { ElMessage } from 'element-plus';
|
|
import ImageUpload from '@/components/ImageUpload/index.vue';
|
|
import { updateAgentFloatBall, getAgentFloatBallConfig } from '@/api/im/agent';
|
|
|
|
// 定义组件名称
|
|
defineOptions({
|
|
name: 'FloatBallSetting'
|
|
});
|
|
|
|
// 定义事件
|
|
const emit = defineEmits<{
|
|
(e: 'update:config', config: FloatBallConfig): void;
|
|
(e: 'save', config: FloatBallConfig): void;
|
|
}>();
|
|
|
|
// 定义props
|
|
interface Props {
|
|
initialConfig?: Partial<FloatBallConfig>;
|
|
token?: string;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
initialConfig: () => ({}),
|
|
token: ''
|
|
});
|
|
|
|
// 悬浮球配置接口
|
|
export interface FloatBallConfig {
|
|
pcImage: string;
|
|
pcImageThumb?: string;
|
|
mobileImage: string;
|
|
mobileImageThumb?: string;
|
|
}
|
|
|
|
// 默认配置
|
|
const defaultConfig: FloatBallConfig = {
|
|
pcImage: '',
|
|
pcImageThumb: '',
|
|
mobileImage: '',
|
|
mobileImageThumb: ''
|
|
};
|
|
|
|
const showSettingDialog = ref(false);
|
|
const floatBallConfig = ref<FloatBallConfig>({ ...defaultConfig });
|
|
|
|
// 从数据库加载配置 ✅ 核心修复
|
|
async function loadConfigFromServer() {
|
|
if (!props.token) return;
|
|
|
|
try {
|
|
const res = await getAgentFloatBallConfig(props.token);
|
|
if (res.data) {
|
|
const serverConfig = {
|
|
pcImage: res.data.pcFloatBall || '',
|
|
pcImageThumb: res.data.pcFloatBall || '',
|
|
mobileImage: res.data.mobileFloatBall || '',
|
|
mobileImageThumb: res.data.mobileFloatBall || ''
|
|
};
|
|
floatBallConfig.value = { ...defaultConfig, ...serverConfig };
|
|
|
|
// 同步到本地存储做兼容
|
|
const storageKey = `floatBallConfig_${props.token}`;
|
|
localStorage.setItem(storageKey, JSON.stringify(floatBallConfig.value));
|
|
|
|
emit('update:config', floatBallConfig.value);
|
|
}
|
|
} catch (err) {
|
|
console.error('加载服务器配置失败', err);
|
|
// 失败则读取本地
|
|
loadFloatBallConfig();
|
|
}
|
|
}
|
|
|
|
// 保存悬浮球配置
|
|
async function saveFloatBallConfig() {
|
|
try {
|
|
if (!props.token) {
|
|
ElMessage.warning('未获取到代理标识,无法保存配置');
|
|
return;
|
|
}
|
|
|
|
// 调用后端接口保存到数据库
|
|
await updateAgentFloatBall({
|
|
uniqueToken: props.token,
|
|
pcFloatBall: floatBallConfig.value.pcImage,
|
|
mobileFloatBall: floatBallConfig.value.mobileImage
|
|
});
|
|
|
|
// 保存到本地
|
|
const storageKey = `floatBallConfig_${props.token}`;
|
|
localStorage.setItem(storageKey, JSON.stringify(floatBallConfig.value));
|
|
|
|
emit('save', floatBallConfig.value);
|
|
emit('update:config', floatBallConfig.value);
|
|
|
|
ElMessage.success('悬浮球设置保存成功!');
|
|
showSettingDialog.value = false;
|
|
} catch (error) {
|
|
console.error(error);
|
|
ElMessage.error('保存失败,请重试');
|
|
}
|
|
}
|
|
|
|
// 加载本地配置(降级方案)
|
|
function loadFloatBallConfig() {
|
|
const storageKey = `floatBallConfig_${props.token || 'default'}`;
|
|
const savedConfig = localStorage.getItem(storageKey);
|
|
|
|
if (savedConfig) {
|
|
try {
|
|
const parsed = JSON.parse(savedConfig);
|
|
floatBallConfig.value = { ...defaultConfig, ...parsed };
|
|
} catch (e) {
|
|
floatBallConfig.value = { ...defaultConfig };
|
|
}
|
|
} else if (props.initialConfig && Object.keys(props.initialConfig).length > 0) {
|
|
floatBallConfig.value = { ...defaultConfig, ...props.initialConfig };
|
|
}
|
|
emit('update:config', floatBallConfig.value);
|
|
}
|
|
|
|
// 监听弹窗打开,加载服务器数据 ✅
|
|
watch(showSettingDialog, (val) => {
|
|
if (val && props.token) {
|
|
loadConfigFromServer();
|
|
}
|
|
});
|
|
|
|
// 监听token变化
|
|
watch(() => props.token, () => {
|
|
if (props.token) {
|
|
loadConfigFromServer();
|
|
} else {
|
|
loadFloatBallConfig();
|
|
}
|
|
});
|
|
|
|
onMounted(() => {
|
|
if (props.token) {
|
|
loadConfigFromServer();
|
|
} else {
|
|
loadFloatBallConfig();
|
|
}
|
|
});
|
|
|
|
defineExpose({
|
|
showDialog: () => {
|
|
showSettingDialog.value = true;
|
|
},
|
|
getConfig: () => floatBallConfig.value,
|
|
setConfig: (config: Partial<FloatBallConfig>) => {
|
|
floatBallConfig.value = { ...floatBallConfig.value, ...config };
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scss>
|
|
.float-ball-setting {
|
|
.setting-header {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-bottom: 16px;
|
|
padding: 0 4px;
|
|
}
|
|
|
|
.upload-section {
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: center;
|
|
|
|
.upload-tip {
|
|
p {
|
|
margin: 0;
|
|
color: #909399;
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.dialog-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 12px;
|
|
}
|
|
}
|
|
</style>
|