解决 Profile.vue 合并冲突,同步master代码

This commit is contained in:
田香琪
2026-07-14 16:45:10 +08:00
42 changed files with 292 additions and 309 deletions

View File

@@ -1,29 +1,29 @@
import {commonUrl, getRequest, getRequestWithNoToken, postRequestWithNoToken,uploadFileRequest,uploadFile} from '@/libs/axios';
import {managerApiUrl, getRequest, getRequestWithNoToken, postRequestWithNoToken,uploadFileRequest,uploadFile} from '@/libs/axios';
// 通过id获取子地区
export const getChildRegion = (id) => {
return getRequest(`${commonUrl}/common/common/region/item/${id}`);
return getRequest(`${managerApiUrl}/manager/common/region/item/${id}`);
};
// 点地图获取地址信息
export const getRegion = (params) => {
return getRequest(`${commonUrl}/common/common/region/region`, params);
return getRequest(`${managerApiUrl}/manager/common/region/region`, params);
};
// 获取拼图验证
export const getVerifyImg = (verificationEnums) => {
return getRequestWithNoToken(`${commonUrl}/common/common/slider/${verificationEnums}`);
return getRequestWithNoToken(`${managerApiUrl}/manager/common/slider/${verificationEnums}`);
};
// 拼图验证
export const postVerifyImg = (params) => {
return postRequestWithNoToken(`${commonUrl}/common/common/slider/${params.verificationEnums}`, params);
return postRequestWithNoToken(`${managerApiUrl}/manager/common/slider/${params.verificationEnums}`, params);
};
// 获取系统基础信息(登录页等场景无需 token
export const getBaseSite = () => {
return getRequestWithNoToken(`${commonUrl}/common/common/site`);
return getRequestWithNoToken(`${managerApiUrl}/manager/common/site`);
};
// 上传文件

View File

@@ -9,20 +9,18 @@ import {
putRequestWithNoForm,
postRequestWithNoTokenData,
postRequestWithNoForm,
managerUrl
managerUrl,
managerApiUrl
} from "@/libs/axios";
let commonUrl = (process.env.NODE_ENV === 'development' ? BASE.API_DEV.common : BASE.API_PROD.common)
// 文件上传接口
export const uploadFile = commonUrl+ "/common/common/upload/file";
export const uploadFile = managerApiUrl + "/manager/common/upload/file";
// Websocket
export const ws = managerUrl + "/ws";
//获取所有city
export const getAllCity = (params) => {
return getRequest(commonUrl + '/common/common/region/allCity', params)
return getRequest(managerApiUrl + '/manager/common/region/allCity', params)
}
@@ -356,7 +354,7 @@ export const aliDownloadFile = (fKey, params) => {
// base64上传
export const base64Upload = (params) => {
return postRequest("/common/common/upload/file", params);
return postRequest("/common/upload/file", params);
};
// 系统设置
@@ -460,31 +458,6 @@ export const resetPassword = (params) => {
return postRequest(`/passport/user/resetPassword/${params}`);
};
// 初始化商品索引
export const createIndex = () => {
return getRequest(`/other/elasticsearch`);
};
// 初始化商品索引
export const getProgress = () => {
return getRequest(`/other/elasticsearch/progress`);
};
// 删除ES中下架的商品
export const deleteGoodsDown = () => {
return getRequest(`/other/elasticsearch/deleteGoodsDown`);
};
// 删除不存在的索引
export const delSkuIndex = () => {
return getRequest(`/other/elasticsearch/delSkuIndex`);
};
// 生成所有商品的缓存
export const generateGoodsCache = () => {
return getRequest(`/other/elasticsearch/cache`);
};
// 分页查询自定义分词
export const getCustomWordsPage = (params) => {
return getRequest(`/other/customWords/page`, params);
@@ -532,22 +505,22 @@ export const getHotWordsHistory = (params) => {
// 获取文件目录列表
export const getFileDirectory = () => {
return getRequest(commonUrl+`/common/resource/fileDirectory`);
return getRequest(managerApiUrl+`/manager/common/fileDirectory`);
};
// 添加文件目录
export const addFileDirectory = (params) => {
return postRequestWithNoForm(commonUrl+`/common/resource/fileDirectory`,params);
return postRequestWithNoForm(managerApiUrl+`/manager/common/fileDirectory`,params);
};
// 修改文件目录
export const updateFileDirectory = (params) => {
return putRequestWithNoForm(commonUrl+`/common/resource/fileDirectory`,params);
return putRequestWithNoForm(managerApiUrl+`/manager/common/fileDirectory`,params);
};
// 删除文件目录
export const delFileDirectory = (id) => {
return deleteRequest(commonUrl+`/common/resource/fileDirectory/${id}`);
return deleteRequest(managerApiUrl+`/manager/common/fileDirectory/${id}`);
};

View File

@@ -1,13 +1,13 @@
import {commonUrl, getRequestWithNoToken, postRequestWithNoToken} from '@/libs/axios';
import {managerApiUrl, getRequestWithNoToken, postRequestWithNoToken} from '@/libs/axios';
// 获取拼图验证
export const getVerifyImg = (verificationEnums) => {
return getRequestWithNoToken(`${commonUrl}/common/common/slider/${verificationEnums}`);
return getRequestWithNoToken(`${managerApiUrl}/manager/common/slider/${verificationEnums}`);
};
// 拼图验证
export const postVerifyImg = (params) => {
return postRequestWithNoToken(`${commonUrl}/common/common/slider/${params.verificationEnums}`, params);
return postRequestWithNoToken(`${managerApiUrl}/manager/common/slider/${params.verificationEnums}`, params);
};

View File

@@ -6,16 +6,13 @@ import Cookies from "js-cookie";
import {v4 as uuidv4} from 'uuid';
// 统一请求路径前缀
export const commonUrl =
export const managerApiUrl =
process.env.NODE_ENV === "development"
? BASE.API_DEV.common
: BASE.API_PROD.common;
export const managerUrl =
(process.env.NODE_ENV === "development"
? BASE.API_DEV.manager
: BASE.API_PROD.manager) + BASE.PREFIX;
: BASE.API_PROD.manager;
export const managerUrl = managerApiUrl + BASE.PREFIX;
// 文件上传接口
export const uploadFile = commonUrl + "/common/common/upload/file";
export const uploadFile = managerApiUrl + "/manager/common/upload/file";
const service = axios.create({

View File

@@ -17,6 +17,17 @@ export interface LiveRoomForm {
endTime?: string
pushStreamServer?: string
pushStreamCode?: string
imSdkAppid?: string | number
imSdkAppId?: string | number
sdkAppId?: string | number
imSdkSecretKey?: string
sdkSecretKey?: string
secretKey?: string
userSig?: string
imUserSig?: string
adminUserSig?: string
imGroupId?: string
groupId?: string
}
export interface LiveCoupon {

View File

@@ -38,9 +38,13 @@
</el-table-column>
<el-table-column label="操作" width="160" fixed="right" align="center">
<template #default="{ row }">
<a class="link-text" @click="handleAuth(row, 'PASS')">通过</a>
<span class="op-split">|</span>
<a class="link-text" @click="handleAuth(row, 'REJECT')">拒绝</a>
<template v-if="canPassAuth(row)">
<a class="link-text" @click="handleAuth(row, 'PASS')">通过</a>
</template>
<template v-if="canPassAuth(row) && canRejectAuth(row)">
<span class="op-split">|</span>
</template>
<a v-if="canRejectAuth(row)" class="link-text" @click="handleAuth(row, 'REFUSE')">拒绝</a>
</template>
</el-table-column>
</el-table>
@@ -82,9 +86,13 @@
</el-table-column>
<el-table-column label="操作" width="160" fixed="right" align="center">
<template #default="{ row }">
<a class="link-text" @click="handleAuth(row, 'PASS')">通过</a>
<span class="op-split">|</span>
<a class="link-text" @click="handleAuth(row, 'REJECT')">拒绝</a>
<template v-if="canPassAuth(row)">
<a class="link-text" @click="handleAuth(row, 'PASS')">通过</a>
</template>
<template v-if="canPassAuth(row) && canRejectAuth(row)">
<span class="op-split">|</span>
</template>
<a v-if="canRejectAuth(row)" class="link-text" @click="handleAuth(row, 'REFUSE')">拒绝</a>
</template>
</el-table-column>
</el-table>
@@ -262,6 +270,14 @@ export default {
const map = { PASS: "success", REFUSE: "danger", TOBEAUDITED: "warning" };
return map[status] || "info";
},
canPassAuth(row) {
const status = row?.authStatus;
return status === "TOBEAUDITED" || status === "REFUSE" || !status;
},
canRejectAuth(row) {
const status = row?.authStatus;
return status === "TOBEAUDITED" || status === "PASS" || !status;
},
parsePageResult(res) {
if (!res) return { records: [], total: 0 };
if (res.records) {
@@ -342,7 +358,10 @@ export default {
});
},
handleAuth(record, authStatus) {
authLiveMessage({ id: record.id, authStatus }).then((res) => {
authLiveMessage({
...record,
authStatus,
}).then((res) => {
if (res.success) {
this.$Message.success("操作成功");
this.loadMessages();

View File

@@ -46,6 +46,7 @@
import Cookies from "js-cookie";
import { toRef } from "vue";
import { useChat } from "../composables/useChat";
import { resolveImConfigFromDetail } from "../composables/useLiveSetting";
export default {
name: "LiveChatPanel",
@@ -71,7 +72,7 @@ export default {
userInfo = {};
}
return useChat(toRef(props, "liveId"), userInfo);
return useChat(toRef(props, "liveId"), userInfo, toRef(props, "liveDetail"));
},
data() {
return {
@@ -84,30 +85,60 @@ export default {
imGroupId() {
return this.liveDetail?.imGroupId || this.liveDetail?.groupId || this.liveId || "";
},
imSdkAppId() {
return resolveImConfigFromDetail(this.liveDetail).sdkAppId;
},
canInitChat() {
const { sdkAppId, secretKey, userSig } = resolveImConfigFromDetail(this.liveDetail);
return !!(this.imGroupId && sdkAppId && (userSig || secretKey));
},
},
watch: {
imGroupId: {
canInitChat: {
immediate: true,
handler(id) {
if (!id) return;
this.setGroupID(id);
handler(ready) {
if (!ready) return;
this.setGroupID(this.imGroupId);
if (this.active) {
this.bootstrapChat();
}
},
},
imGroupId(id) {
if (!id) return;
this.setGroupID(id);
},
active(val) {
if (val && this.imGroupId && !this.chatInited) {
if (val && this.canInitChat && !this.chatInited) {
this.bootstrapChat();
}
},
liveId() {
this.chatInited = false;
this.cleanup();
if (this.active && this.imGroupId) {
if (this.active && this.canInitChat) {
this.bootstrapChat();
}
},
liveDetail: {
deep: true,
handler(detail, oldDetail) {
const nextConfig = resolveImConfigFromDetail(detail);
const prevConfig = resolveImConfigFromDetail(oldDetail);
if (
nextConfig.sdkAppId &&
(nextConfig.sdkAppId !== prevConfig.sdkAppId ||
nextConfig.secretKey !== prevConfig.secretKey ||
nextConfig.userSig !== prevConfig.userSig)
) {
this.chatInited = false;
this.cleanup();
if (this.active && this.canInitChat) {
this.bootstrapChat();
}
}
},
},
},
beforeUnmount() {
this.cleanup();
@@ -117,7 +148,7 @@ export default {
this.createTextMessageByInput();
},
async bootstrapChat() {
if (!this.liveId || this.chatInited || this.initializing) {
if (!this.liveId || this.chatInited || this.initializing || !this.canInitChat) {
return;
}

View File

@@ -3,13 +3,13 @@ import { nextTick, ref, unref, watch } from "vue";
import { postSendMessage } from "@/api/live";
import { Message } from "@/utils/message";
import TencentCloudChat from "@tencentcloud/chat";
import { fetchLiveSetting } from "./useLiveSetting";
import { resolveImConfigFromDetail } from "./useLiveSetting";
import { genTestUserSig } from "../utils/GenerateTestUserSig";
const MAX_MESSAGE_COUNT = 100;
const ADMIN_IM_USER_ID = "admin";
export function useChat(liveId, userInfo) {
export function useChat(liveId, userInfo, liveDetail) {
const chat = ref(null);
const groupID = ref("");
const messageList = ref([]);
@@ -67,22 +67,25 @@ export function useChat(liveId, userInfo) {
return;
}
const setting = await fetchLiveSetting();
if (!setting?.imSdkAppid) {
initError.value = "请先在系统设置中配置直播 IM SDK APPID";
return;
}
if (!setting?.imSdkSecretKey) {
initError.value = "请先在系统设置中配置直播 IM SDK 密钥";
const detail = unref(liveDetail) || {};
const { sdkAppId, secretKey, userSig: detailUserSig } = resolveImConfigFromDetail(detail);
if (!sdkAppId) {
initError.value = "直播间详情缺少 IM SDK APPID";
return;
}
const sdkAppId = Number(setting.imSdkAppid);
const { userSig } = genTestUserSig({
SDKAppID: sdkAppId,
secretKey: setting.imSdkSecretKey,
userID: ADMIN_IM_USER_ID,
});
let userSig = detailUserSig;
if (!userSig) {
if (!secretKey) {
initError.value = "直播间详情缺少 IM 登录凭证";
return;
}
userSig = genTestUserSig({
SDKAppID: sdkAppId,
secretKey,
userID: ADMIN_IM_USER_ID,
}).userSig;
}
if (chat.value) {
detachChatEvents(chat.value);

View File

@@ -34,7 +34,7 @@ export async function fetchLiveSetting(force = false) {
return fetchingPromise;
}
/** 解析 IM SDK AppID */
/** 解析 IM SDK AppID(系统设置,兼容旧逻辑) */
export function resolveSdkAppId(setting) {
if (!setting?.imSdkAppid) {
return 0;
@@ -42,3 +42,30 @@ export function resolveSdkAppId(setting) {
const id = Number(setting.imSdkAppid);
return Number.isNaN(id) ? 0 : id;
}
/** 从直播间详情解析 IM SDK AppID */
export function resolveSdkAppIdFromDetail(detail) {
return resolveImConfigFromDetail(detail).sdkAppId;
}
/** 从直播间详情解析 IM 配置 */
export function resolveImConfigFromDetail(detail) {
if (!detail) {
return { sdkAppId: 0, secretKey: "", userSig: "" };
}
const sdkRaw = detail.sdkAppId ?? detail.imSdkAppId ?? detail.imSdkAppid;
const sdkAppId =
sdkRaw == null || sdkRaw === "" ? 0 : Number(sdkRaw);
const secretKey =
detail.imSdkSecretKey ?? detail.sdkSecretKey ?? detail.secretKey ?? "";
const userSig =
detail.userSig ?? detail.imUserSig ?? detail.adminUserSig ?? "";
return {
sdkAppId: Number.isNaN(sdkAppId) ? 0 : sdkAppId,
secretKey: String(secretKey || ""),
userSig: String(userSig || ""),
};
}

View File

@@ -27,7 +27,7 @@
<div class="panel-head">
<span class="panel-title">直播预览</span>
<div class="ratio-switch">
</div>
</div>
<div class="player-box" :class="aspectRatio === '16:9' ? 'ratio-169' : 'ratio-43'">
@@ -239,7 +239,6 @@
</template>
<script>
import { getSetting } from "@/api/index";
import {
endLive,
getLiveInfo,
@@ -363,7 +362,6 @@ export default {
this.$Message.error("缺少直播ID");
return;
}
this.loadWapSetting();
this.refreshAll();
this.startDurationTimer();
},
@@ -644,7 +642,7 @@ export default {
},
buildShareUrl() {
const base = (this.wapUrl || BASE?.WAP_URL || config.website || "").replace(/\/?$/, "/");
return `${base}pages/promotions/live/detail?id=${this.liveId}`;
return `${base}pages/promotion/live/room?id=${this.liveId}`;
},
handleShare() {
const url = this.buildShareUrl();
@@ -692,15 +690,6 @@ export default {
},
});
},
loadWapSetting() {
getSetting("BASE_SETTING")
.then((res) => {
if (res.success && res.result?.buyerDomain) {
this.wapUrl = res.result.buyerDomain;
}
})
.catch(() => {});
},
},
};
</script>

View File

@@ -237,7 +237,7 @@ export default {
},
buildH5ShareUrl(liveId) {
const base = (this.wapUrl || BASE?.WAP_URL || config.website || "").replace(/\/?$/, "/");
return `${base}pages/promotions/live/detail?id=${liveId}`;
return `${base}pages/promotion/live/room?id=${liveId}`;
},
handleShare(row) {
this.shareTitle = row.title || "";
@@ -246,8 +246,40 @@ export default {
},
copyShareUrl() {
if (!this.shareH5Url) return;
navigator.clipboard.writeText(this.shareH5Url);
this.$Message.success("链接已复制");
this.copyText(this.shareH5Url);
},
copyText(text) {
const onSuccess = () => this.$Message.success("链接已复制");
const onFail = () => this.$Message.error("复制失败,请手动复制");
if (navigator.clipboard?.writeText) {
navigator.clipboard
.writeText(text)
.then(onSuccess)
.catch(() => {
this.copyTextFallback(text) ? onSuccess() : onFail();
});
return;
}
this.copyTextFallback(text) ? onSuccess() : onFail();
},
copyTextFallback(text) {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-9999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
let ok = false;
try {
ok = document.execCommand("copy");
} catch (e) {
ok = false;
}
document.body.removeChild(textArea);
return ok;
},
handleStart(row) {
if (row.liveStatus === "LIVING") {

View File

@@ -190,7 +190,7 @@
<el-upload
v-if="canUploadImages"
ref="up"
:action="commonUrl + '/common/common/upload/file'"
:action="managerApiUrl + '/manager/common/upload/file'"
:data="uploadData"
:headers="accessToken"
:before-upload="beforeUpload"
@@ -385,7 +385,7 @@ import {
updateFileDirectory,
} from "@/api/index";
import DPlayer from "dplayer";
import {commonUrl} from "@/libs/axios";
import {managerApiUrl} from "@/libs/axios";
import config from "@/config/index";
import playIcon from "@/assets/play.png";
@@ -417,7 +417,7 @@ export default {
},
data() {
return {
commonUrl, // 上传文件路径
managerApiUrl, // 上传文件路径
config, // api地址
fileDirectoryId: "",
groupFormValidate: {

View File

@@ -51,15 +51,6 @@
</el-form-item>
</div>
<div class="label-btns goods-setting-btns">
<div class="goods-setting-action-row">
<el-button type="success" @click="createIndex()">重新生成所有商品索引</el-button>
<el-button type="info" @click="deleteDownGoods()">删除ES中下架的商品</el-button>
<el-button type="warning" @click="generateCache()">生成所有商品的缓存</el-button>
<el-button type="danger" @click="deleteNotExistIndex()">删除不存在的索引</el-button>
<div class="progress-item" v-if="showProgress">
<el-progress :percentage="progressVal" />
</div>
</div>
<div class="goods-setting-save-row">
<el-button type="primary" @click="submit('formValidate')">保存</el-button>
</div>
@@ -69,7 +60,7 @@
</template>
<script>
import { ElMessage } from "element-plus";
import { setSetting, createIndex, getProgress, deleteGoodsDown, generateGoodsCache, delSkuIndex } from "@/api/index";
import { setSetting } from "@/api/index";
export default {
props: ["res", "type"],
data() {
@@ -83,9 +74,6 @@ export default {
originalPictureWidth: "0",
originalPictureHeight: "0",
},
progressVal: 0,
showProgress: false,
intervalProgress: null,
ruleValidate: {},
result: "",
};
@@ -103,69 +91,6 @@ export default {
}
});
},
createIndex() {
createIndex().then((res) => {
if (res.success) {
ElMessage.success("开始生成!");
this.showProgress = true;
setTimeout(() => {
this.intervalProgress = setInterval(() => {
getProgress().then((resp) => {
const progressResult = resp.result;
if (progressResult != null && progressResult.flag === 0) {
clearInterval(this.intervalProgress);
this.showProgress = false;
ElMessage.success("生成成功!");
} else {
this.progressVal = Math.floor((progressResult.processed / progressResult.total) * 100);
}
});
}, 1000);
}, 10000);
} else if (res.code === 100000) {
this.showProgress = true;
this.intervalProgress = setInterval(() => {
getProgress().then((resp) => {
const progressResult = resp.result;
if (progressResult != null && progressResult.flag === 0) {
clearInterval(this.intervalProgress);
this.showProgress = false;
ElMessage.success("生成成功!");
} else {
this.progressVal = Math.floor((progressResult.processed / progressResult.total) * 100);
}
});
}, 1000);
}
});
},
deleteDownGoods() {
deleteGoodsDown().then((res) => {
if (res.success) {
ElMessage.success("删除成功!");
} else {
ElMessage.error("删除失败!");
}
});
},
generateCache() {
generateGoodsCache().then((res) => {
if (res.success) {
ElMessage.success("生成成功!");
} else {
ElMessage.error("生成失败!");
}
});
},
deleteNotExistIndex() {
delSkuIndex().then((res) => {
if (res.success) {
ElMessage.success("删除成功!");
} else {
ElMessage.error("删除失败!");
}
});
},
setupSetting() {
setSetting(this.type, this.formValidate).then((res) => {
if (res.success) {
@@ -217,19 +142,6 @@ export default {
gap: 10px;
margin-left: 150px;
}
.goods-setting-action-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.goods-setting-action-row .progress-item {
flex: 0 0 240px;
min-width: 200px;
}
.goods-setting-action-row .progress-item :deep(.el-progress) {
width: 100%;
}
:deep(.el-input){
width: 180px !important;
}