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

<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>