style:样式切换

This commit is contained in:
lqins
2024-12-19 16:57:57 +08:00
parent 710b008453
commit 357c77ef30
59 changed files with 4775 additions and 3420 deletions

View File

@@ -256,7 +256,7 @@ const reGenerate = (prompt) => {
code {
color:var(--theme-text-color-primary);
background-color #e7e7e8
background-color var(--el-color-primary-light-3)
padding 0 3px;
border-radius 5px;
}
@@ -348,7 +348,7 @@ const reGenerate = (prompt) => {
padding 10px 10px 10px 0;
.bar-item {
background-color #e7e7e8;
background-color var( --little-btn-bg);
color #888
padding 3px 5px;
margin-right 10px;
@@ -433,7 +433,7 @@ const reGenerate = (prompt) => {
code {
color:var(--theme-text-color-primary);
background-color #e7e7e8
background-color var( --little-btn-bg)
padding 0 3px;
border-radius 5px;
}
@@ -539,7 +539,7 @@ const reGenerate = (prompt) => {
}
.bar-item.bg {
background-color #e7e7e8
background-color var( --gray-btn-bg)
cursor pointer
}

View File

@@ -1,76 +1,90 @@
<template>
<div class="invite-list" v-loading="loading">
<el-row v-if="items.length > 0">
<el-table :data="items" :row-key="row => row.id" table-layout="auto" border
style="--el-table-border-color:#373C47;
--el-table-tr-bg-color:#2D323B;
--el-table-row-hover-bg-color:#373C47;
--el-table-header-bg-color:#474E5C;
--el-table-text-color:#d1d1d1">
<el-table-column prop="username" label="用户"/>
<el-table-column prop="invite_code" label="邀请码"/>
<el-table-column prop="remark" label="邀请奖励"/>
<el-table
:data="items"
:row-key="(row) => row.id"
table-layout="auto"
border
style="
--el-table-border-color: #373c47;
--el-table-tr-bg-color: #2d323b;
--el-table-row-hover-bg-color: #373c47;
--el-table-header-bg-color: #474e5c;
--el-table-text-color: #d1d1d1;
"
>
<el-table-column prop="username" label="用户" />
<el-table-column prop="invite_code" label="邀请码" />
<el-table-column prop="remark" label="邀请奖励" />
<el-table-column label="注册时间">
<template #default="scope">
<span>{{ dateFormat(scope.row['created_at']) }}</span>
<span>{{ dateFormat(scope.row["created_at"]) }}</span>
</template>
</el-table-column>
</el-table>
</el-row>
<el-empty :image-size="100" v-else/>
<el-empty :image-size="100" :image="nodata" description="暂无数据" v-else />
<div class="pagination">
<el-pagination v-if="total > 0" background
layout="total,prev, pager, next"
:hide-on-single-page="true"
v-model:current-page="page"
v-model:page-size="pageSize"
@current-change="fetchData()"
:total="total"/>
<el-pagination
v-if="total > 0"
background
layout="total,prev, pager, next"
:hide-on-single-page="true"
v-model:current-page="page"
v-model:page-size="pageSize"
style="--el-pagination-button-bg-color: rgba(86, 86, 95, 0.2)"
@current-change="fetchData()"
:total="total"
/>
</div>
</div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import {httpGet} from "@/utils/http";
import {ElMessage} from "element-plus";
import {dateFormat} from "@/utils/libs";
import nodata from "@/assets/img/no-data.png";
import { onMounted, ref } from "vue";
import { httpGet } from "@/utils/http";
import { ElMessage } from "element-plus";
import { dateFormat } from "@/utils/libs";
import Clipboard from "clipboard";
const items = ref([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(10)
const loading = ref(true)
const items = ref([]);
const total = ref(0);
const page = ref(1);
const pageSize = ref(10);
const loading = ref(true);
onMounted(() => {
fetchData()
const clipboard = new Clipboard('.copy-order-no');
clipboard.on('success', () => {
fetchData();
const clipboard = new Clipboard(".copy-order-no");
clipboard.on("success", () => {
ElMessage.success("复制成功");
})
});
clipboard.on('error', () => {
ElMessage.error('复制失败');
})
})
clipboard.on("error", () => {
ElMessage.error("复制失败");
});
});
// 获取数据
const fetchData = () => {
httpGet('/api/invite/list', {page: page.value, page_size: pageSize.value}).then((res) => {
if (res.data) {
items.value = res.data.items
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.page_size
}
loading.value = false
}).catch(e => {
ElMessage.error("获取数据失败" + e.message);
})
}
httpGet("/api/invite/list", { page: page.value, page_size: pageSize.value })
.then((res) => {
if (res.data) {
items.value = res.data.items;
total.value = res.data.total;
page.value = res.data.page;
pageSize.value = res.data.page_size;
}
loading.value = false;
})
.catch((e) => {
ElMessage.error("获取数据失败" + e.message);
});
};
</script>
<style scoped lang="stylus">
@@ -90,4 +104,4 @@ const fetchData = () => {
color #20a0ff
}
}
</style>
</style>

View File

@@ -1,5 +1,5 @@
<template>
<el-container class="realtime-conversation" :style="{height: height}">
<el-container class="realtime-conversation" :style="{ height: height }">
<!-- connection animation -->
<el-container class="connection-container" v-if="!isConnected">
<div class="phone-container">
@@ -36,14 +36,18 @@
</div>
</div>
<div class="call-controls">
<el-tooltip content="长按发送语音" placement="top" effect="light">
<el-tooltip content="长按发送语音" placement="top">
<ripple-button>
<button class="call-button answer" @mousedown="startRecording" @mouseup="stopRecording">
<button
class="call-button answer"
@mousedown="startRecording"
@mouseup="stopRecording"
>
<i class="iconfont icon-mic-bold"></i>
</button>
</ripple-button>
</el-tooltip>
<el-tooltip content="结束通话" placement="top" effect="light">
<el-tooltip content="结束通话" placement="top">
<button class="call-button hangup" @click="hangUp">
<i class="iconfont icon-hung-up"></i>
</button>
@@ -51,32 +55,31 @@
</div>
</div>
</el-container>
</template>
<script setup>
import RippleButton from "@/components/ui/RippleButton.vue";
import { ref, onMounted, onUnmounted } from 'vue';
import { RealtimeClient } from '@openai/realtime-api-beta';
import { WavRecorder, WavStreamPlayer } from '@/lib/wavtools/index.js';
import { instructions } from '@/utils/conversation_config.js';
import { WavRenderer } from '@/utils/wav_renderer';
import {showMessageError} from "@/utils/dialog";
import {getUserToken} from "@/store/session";
import { ref, onMounted, onUnmounted } from "vue";
import { RealtimeClient } from "@openai/realtime-api-beta";
import { WavRecorder, WavStreamPlayer } from "@/lib/wavtools/index.js";
import { instructions } from "@/utils/conversation_config.js";
import { WavRenderer } from "@/utils/wav_renderer";
import { showMessageError } from "@/utils/dialog";
import { getUserToken } from "@/store/session";
// eslint-disable-next-line no-unused-vars,no-undef
const props = defineProps({
height: {
type: String,
default: '100vh'
default: "100vh"
}
})
});
// eslint-disable-next-line no-undef
const emits = defineEmits(['close']);
const emits = defineEmits(["close"]);
/********************** connection animation code *************************/
const fullText = "正在接通中...";
const connectingText = ref("")
const connectingText = ref("");
let index = 0;
const typeText = () => {
if (index < fullText.length) {
@@ -85,12 +88,12 @@ const typeText = () => {
setTimeout(typeText, 200); // 每300毫秒显示一个字
} else {
setTimeout(() => {
connectingText.value = '';
connectingText.value = "";
index = 0;
typeText();
}, 1000); // 等待1秒后重新开始
}
}
};
/*************************** end of code ****************************************/
/********************** conversation process code ***************************/
@@ -102,31 +105,29 @@ const animateVoice = () => {
rightVoiceActive.value = Math.random() > 0.5;
};
const wavRecorder = ref(new WavRecorder({ sampleRate: 24000 }));
const wavStreamPlayer = ref(new WavStreamPlayer({ sampleRate: 24000 }));
let host = process.env.VUE_APP_WS_HOST
if (host === '') {
if (location.protocol === 'https:') {
host = 'wss://' + location.host;
let host = process.env.VUE_APP_WS_HOST;
if (host === "") {
if (location.protocol === "https:") {
host = "wss://" + location.host;
} else {
host = 'ws://' + location.host;
host = "ws://" + location.host;
}
}
const client = ref(
new RealtimeClient({
url: `${host}/api/realtime`,
apiKey: getUserToken(),
dangerouslyAllowAPIKeyInBrowser: true,
})
new RealtimeClient({
url: `${host}/api/realtime`,
apiKey: getUserToken(),
dangerouslyAllowAPIKeyInBrowser: true
})
);
// // Set up client instructions and transcription
client.value.updateSession({
instructions: instructions,
turn_detection: null,
input_audio_transcription: { model: 'whisper-1' },
voice: 'alloy',
input_audio_transcription: { model: "whisper-1" },
voice: "alloy"
});
// set voice wave canvas
@@ -137,62 +138,66 @@ const isRecording = ref(false);
const backgroundAudio = ref(null);
const hangUpAudio = ref(null);
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
return new Promise((resolve) => setTimeout(resolve, ms));
}
const connect = async () => {
if (isConnected.value) {
return
return;
}
// 播放背景音乐
if (backgroundAudio.value) {
backgroundAudio.value.play().catch(error => {
console.error('播放失败,可能是浏览器的自动播放策略导致的:', error);
backgroundAudio.value.play().catch((error) => {
console.error("播放失败,可能是浏览器的自动播放策略导致的:", error);
});
}
// 模拟拨号延时
await sleep(3000)
await sleep(3000);
try {
await client.value.connect();
await wavRecorder.value.begin();
await wavStreamPlayer.value.connect();
console.log("对话连接成功!")
console.log("对话连接成功!");
if (!client.value.isConnected()) {
return
return;
}
isConnected.value = true;
backgroundAudio.value?.pause()
backgroundAudio.value.currentTime = 0
backgroundAudio.value?.pause();
backgroundAudio.value.currentTime = 0;
client.value.sendUserMessageContent([
{
type: 'input_text',
text: '你好,我是极客学长!',
},
type: "input_text",
text: "你好,我是极客学长!"
}
]);
if (client.value.getTurnDetectionType() === 'server_vad') {
await wavRecorder.value.record((data) => client.value.appendInputAudio(data.mono));
if (client.value.getTurnDetectionType() === "server_vad") {
await wavRecorder.value.record((data) =>
client.value.appendInputAudio(data.mono)
);
}
} catch (e) {
console.error(e)
console.error(e);
}
};
// 开始语音输入
const startRecording = async () => {
if (isRecording.value) {
return
return;
}
isRecording.value = true;
try {
const trackSampleOffset = await wavStreamPlayer.value.interrupt();
if (trackSampleOffset?.trackId) {
const { trackId, offset } = trackSampleOffset;
client.value.cancelResponse(trackId, offset);
}
await wavRecorder.value.record((data) => client.value.appendInputAudio(data.mono));
const trackSampleOffset = await wavStreamPlayer.value.interrupt();
if (trackSampleOffset?.trackId) {
const { trackId, offset } = trackSampleOffset;
client.value.cancelResponse(trackId, offset);
}
await wavRecorder.value.record((data) =>
client.value.appendInputAudio(data.mono)
);
} catch (e) {
console.error(e)
console.error(e);
}
};
@@ -203,7 +208,7 @@ const stopRecording = async () => {
await wavRecorder.value.pause();
client.value.createResponse();
} catch (e) {
console.error(e)
console.error(e);
}
};
@@ -232,13 +237,13 @@ const initialize = async () => {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
const ctx = canvas.getContext('2d');
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const result = wavRecorder.value.recording
? wavRecorder.value.getFrequencies('voice')
: { values: new Float32Array([0]) };
WavRenderer.drawBars(canvas, ctx, result.values, '#0099ff', 10, 0, 8);
? wavRecorder.value.getFrequencies("voice")
: { values: new Float32Array([0]) };
WavRenderer.drawBars(canvas, ctx, result.values, "#0099ff", 10, 0, 8);
}
}
if (serverCanvasRef.value) {
@@ -247,13 +252,13 @@ const initialize = async () => {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
const ctx = canvas.getContext('2d');
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const result = wavStreamPlayer.value.analyser
? wavStreamPlayer.value.getFrequencies('voice')
: { values: new Float32Array([0]) };
WavRenderer.drawBars(canvas, ctx, result.values, '#009900', 10, 0, 8);
? wavStreamPlayer.value.getFrequencies("voice")
: { values: new Float32Array([0]) };
WavRenderer.drawBars(canvas, ctx, result.values, "#009900", 10, 0, 8);
}
}
requestAnimationFrame(render);
@@ -261,17 +266,17 @@ const initialize = async () => {
};
render();
client.value.on('error', (event) => {
showMessageError(event.error)
client.value.on("error", (event) => {
showMessageError(event.error);
});
client.value.on('realtime.event', (re) => {
if (re.event.type === 'error') {
showMessageError(re.event.error)
client.value.on("realtime.event", (re) => {
if (re.event.type === "error") {
showMessageError(re.event.error);
}
});
client.value.on('conversation.interrupted', async () => {
client.value.on("conversation.interrupted", async () => {
const trackSampleOffset = await wavStreamPlayer.value.interrupt();
if (trackSampleOffset?.trackId) {
const { trackId, offset } = trackSampleOffset;
@@ -279,21 +284,20 @@ const initialize = async () => {
}
});
client.value.on('conversation.updated', async ({ item, delta }) => {
client.value.on("conversation.updated", async ({ item, delta }) => {
// console.log('item updated', item, delta)
if (delta?.audio) {
wavStreamPlayer.value.add16BitPCM(delta.audio, item.id);
}
});
}
};
const voiceInterval = ref(null);
onMounted(() => {
initialize()
initialize();
// 启动聊天进行中的动画
voiceInterval.value = setInterval(animateVoice, 200);
typeText()
typeText();
});
onUnmounted(() => {
@@ -304,32 +308,31 @@ onUnmounted(() => {
// 挂断通话
const hangUp = async () => {
try {
isConnected.value = false
isConnected.value = false;
// 停止播放拨号音乐
if (backgroundAudio.value?.currentTime) {
backgroundAudio.value?.pause()
backgroundAudio.value.currentTime = 0
backgroundAudio.value?.pause();
backgroundAudio.value.currentTime = 0;
}
// 断开客户端的连接
client.value.reset()
client.value.reset();
// 中断语音输入和输出服务
await wavRecorder.value.end()
await wavStreamPlayer.value.interrupt()
await wavRecorder.value.end();
await wavStreamPlayer.value.interrupt();
} catch (e) {
console.error(e)
console.error(e);
} finally {
// 播放挂断音乐
hangUpAudio.value?.play()
emits('close')
hangUpAudio.value?.play();
emits("close");
}
};
// eslint-disable-next-line no-undef
defineExpose({ connect,hangUp });
defineExpose({ connect, hangUp });
</script>
<style scoped lang="stylus">
@import "@/assets/css/realtime.styl"
</style>
</style>

View File

@@ -3,12 +3,14 @@
<div class="running-job-box" v-if="list.length > 0">
<div class="job-item" v-for="item in list" :key="item.id">
<div v-if="item.progress > 0" class="job-item-inner">
<div class="progress" v-if="item.progress > 0">
<el-progress type="circle" :percentage="item.progress" :width="100"
color="#47fff1"/>
</div>
<div class="progress" v-if="item.progress > 0">
<el-progress
type="circle"
:percentage="item.progress"
:width="100"
color="#47fff1"
/>
</div>
</div>
<el-image fit="cover" v-else>
<template #error>
@@ -20,21 +22,22 @@
</el-image>
</div>
</div>
<el-empty :image-size="100" v-else/>
<el-empty :image-size="100" v-else :image="nodata" description="暂无任务" />
</div>
</template>
<script setup>
import nodata from "@/assets/img/no-data.png";
// eslint-disable-next-line no-undef
const props = defineProps({
list: {
type: Array,
default:[],
default: []
}
})
});
</script>
<style scoped lang="stylus">
@import "~@/assets/css/running-job-list.styl"
</style>
</style>

View File

@@ -1,95 +1,111 @@
<template>
<div class="user-bill" v-loading="loading" element-loading-background="rgba(255,255,255,.3)">
<div
class="user-bill"
v-loading="loading"
element-loading-background="rgba(255,255,255,.3)"
>
<el-row v-if="items.length > 0">
<el-table :data="items" :row-key="row => row.id" table-layout="auto" border
style="--el-table-border-color:#373C47;
--el-table-tr-bg-color:#2D323B;
--el-table-row-hover-bg-color:#373C47;
--el-table-header-bg-color:#474E5C;
--el-table-text-color:#d1d1d1">
<el-table
:data="items"
:row-key="(row) => row.id"
table-layout="auto"
border
>
<el-table-column prop="order_no" label="订单号">
<template #default="scope">
<span>{{ scope.row.order_no }}</span>
<el-icon class="copy-order-no" :data-clipboard-text="scope.row.order_no">
<DocumentCopy/>
<el-icon
class="copy-order-no"
:data-clipboard-text="scope.row.order_no"
>
<DocumentCopy />
</el-icon>
</template>
</el-table-column>
<el-table-column prop="subject" label="产品名称"/>
<el-table-column prop="amount" label="订单金额"/>
<el-table-column prop="subject" label="产品名称" />
<el-table-column prop="amount" label="订单金额" />
<el-table-column label="订单算力">
<template #default="scope">
<span>{{ scope.row.remark?.power }}</span>
</template>
</el-table-column>
<el-table-column prop="pay_method" label="支付渠道"/>
<el-table-column prop="pay_name" label="支付名称"/>
<el-table-column prop="pay_method" label="支付渠道" />
<el-table-column prop="pay_name" label="支付名称" />
<el-table-column label="支付时间">
<template #default="scope">
<span v-if="scope.row['pay_time']">{{ dateFormat(scope.row['pay_time']) }}</span>
<span v-if="scope.row['pay_time']">{{
dateFormat(scope.row["pay_time"])
}}</span>
<el-tag v-else>未支付</el-tag>
</template>
</el-table-column>
</el-table>
</el-row>
<el-empty :image-size="100" v-else/>
<el-empty :image-size="100" v-else />
<div class="pagination">
<el-pagination v-if="total > 0" background
layout="total,prev, pager, next"
:hide-on-single-page="true"
v-model:current-page="page"
v-model:page-size="pageSize"
@current-change="fetchData()"
:total="total"/>
<el-pagination
v-if="total > 0"
background
layout="total,prev, pager, next"
:hide-on-single-page="true"
v-model:current-page="page"
v-model:page-size="pageSize"
@current-change="fetchData()"
style="--el-pagination-button-bg-color: rgba(86, 86, 95, 0.2)"
:total="total"
/>
</div>
</div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import {httpGet} from "@/utils/http";
import {ElMessage} from "element-plus";
import {dateFormat} from "@/utils/libs";
import {DocumentCopy} from "@element-plus/icons-vue";
import { onMounted, ref } from "vue";
import { httpGet } from "@/utils/http";
import { ElMessage } from "element-plus";
import { dateFormat } from "@/utils/libs";
import { DocumentCopy } from "@element-plus/icons-vue";
import Clipboard from "clipboard";
const items = ref([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(12)
const loading = ref(true)
const items = ref([]);
const total = ref(0);
const page = ref(1);
const pageSize = ref(12);
const loading = ref(true);
onMounted(() => {
fetchData()
const clipboard = new Clipboard('.copy-order-no');
clipboard.on('success', () => {
fetchData();
const clipboard = new Clipboard(".copy-order-no");
clipboard.on("success", () => {
ElMessage.success("复制成功");
})
});
clipboard.on('error', () => {
ElMessage.error('复制失败');
})
})
clipboard.on("error", () => {
ElMessage.error("复制失败");
});
});
// 获取数据
const fetchData = () => {
httpGet('/api/order/list', {page: page.value, page_size: pageSize.value}).then((res) => {
if (res.data) {
items.value = res.data.items
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.page_size
}
loading.value = false
}).catch(e => {
ElMessage.error("获取数据失败" + e.message);
})
}
httpGet("/api/order/list", { page: page.value, page_size: pageSize.value })
.then((res) => {
if (res.data) {
items.value = res.data.items;
total.value = res.data.total;
page.value = res.data.page;
pageSize.value = res.data.page_size;
}
loading.value = false;
})
.catch((e) => {
ElMessage.error("获取数据失败" + e.message);
});
};
</script>
<style scoped lang="stylus">
.user-bill {
background-color: var(--chat-bg);
.pagination {
margin: 20px 0 0 0;
display: flex;
@@ -105,4 +121,4 @@ const fetchData = () => {
color #20a0ff
}
}
</style>
</style>

View File

@@ -3,77 +3,92 @@
<el-form :model="user" label-width="100px">
<el-row>
<el-upload
class="avatar-uploader"
:auto-upload="true"
:show-file-list="false"
:http-request="afterRead"
accept=".png,.jpg,.jpeg,.bmp"
class="avatar-uploader"
:auto-upload="true"
:show-file-list="false"
:http-request="afterRead"
accept=".png,.jpg,.jpeg,.bmp"
>
<el-avatar v-if="user.avatar" :src="user.avatar" shape="circle" :size="100"/>
<el-avatar
v-if="user.avatar"
:src="user.avatar"
shape="circle"
:size="100"
/>
<el-icon v-else class="avatar-uploader-icon">
<Plus/>
<Plus />
</el-icon>
</el-upload>
</el-row>
<el-form-item label="昵称">
<el-input v-model="user['nickname']"/>
<el-input v-model="user['nickname']" />
</el-form-item>
<el-form-item label="账号">
<span>{{ user.username }}</span>
<el-tooltip
<div class="flex">
<span>{{ user.username }}</span>
<el-tooltip
class="box-item"
effect="light"
content="您已经是 VIP 会员"
placement="right"
>
<span class="vip-icon"><el-image v-if="user.vip" :src="vipImg" style="height: 25px;margin-left: 10px"/></span>
</el-tooltip>
>
<span class="vip-icon"
><el-image
v-if="user.vip"
:src="vipImg"
style="height: 25px; margin-left: 10px"
/></span>
</el-tooltip>
</div>
</el-form-item>
<el-form-item label="剩余算力">
<el-tag>{{ user['power'] }}</el-tag>
<el-text type="warning">{{ user["power"] }}</el-text>
</el-form-item>
<el-form-item label="会员到期时间" v-if="user['expired_time'] > 0">
<el-tag type="danger">{{ dateFormat(user['expired_time']) }}</el-tag>
<el-form-item label="会员到期时间" v-if="user['expired_time'] > 0">
<el-tag type="danger">{{ dateFormat(user["expired_time"]) }}</el-tag>
</el-form-item>
<el-row class="opt-line">
<el-button color="#47fff1" :dark="false" @click="save">保存</el-button>
<el-button :dark="false" type="primary" @click="save">保存</el-button>
</el-row>
</el-form>
</div>
</template>
<script setup>
import {onMounted, ref} from "vue"
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage} from "element-plus";
import {Plus} from "@element-plus/icons-vue";
import { onMounted, ref } from "vue";
import { httpGet, httpPost } from "@/utils/http";
import { ElMessage } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import Compressor from "compressorjs";
import {dateFormat} from "@/utils/libs";
import {checkSession} from "@/store/cache";
import { dateFormat } from "@/utils/libs";
import { checkSession } from "@/store/cache";
const user = ref({
vip: false,
username: '演示数据',
nickname: '演示数据',
avatar: '/images/vip.png',
mobile: '演示数据',
power: 99999,
})
const vipImg = ref("/images/vip.png")
username: "演示数据",
nickname: "演示数据",
avatar: "/images/menu/member.png",
mobile: "演示数据",
power: 99999
});
const vipImg = ref("/images/menu/member.png");
onMounted(() => {
checkSession().then(() => {
// 获取最新用户信息
httpGet('/api/user/profile').then(res => {
user.value = res.data
}).catch(e => {
ElMessage.error("获取用户信息失败:" + e.message)
checkSession()
.then(() => {
// 获取最新用户信息
httpGet("/api/user/profile")
.then((res) => {
user.value = res.data;
})
.catch((e) => {
ElMessage.error("获取用户信息失败:" + e.message);
});
})
.catch((e) => {
console.log(e);
});
}).catch(e => {
console.log(e)
})
})
});
const afterRead = (file) => {
// 压缩图片并上传
@@ -81,28 +96,32 @@ const afterRead = (file) => {
quality: 0.6,
success(result) {
const formData = new FormData();
formData.append('file', result, result.name);
formData.append("file", result, result.name);
// 执行上传操作
httpPost('/api/upload', formData).then((res) => {
user.value.avatar = res.data.url
ElMessage.success({message: "上传成功", duration: 500})
}).catch((e) => {
ElMessage.error('图片上传失败:' + e.message)
})
httpPost("/api/upload", formData)
.then((res) => {
user.value.avatar = res.data.url;
ElMessage.success({ message: "上传成功", duration: 500 });
})
.catch((e) => {
ElMessage.error("图片上传失败:" + e.message);
});
},
error(err) {
console.log(err.message);
},
}
});
};
const save = () => {
httpPost('/api/user/profile/update', user.value).then(() => {
ElMessage.success({message: '更新成功', duration: 500})
}).catch((e) => {
ElMessage.error('更新失败:' + e.message)
})
}
httpPost("/api/user/profile/update", user.value)
.then(() => {
ElMessage.success({ message: "更新成功", duration: 500 });
})
.catch((e) => {
ElMessage.error("更新失败:" + e.message);
});
};
</script>
<style lang="stylus" scoped>
@@ -127,4 +146,4 @@ const save = () => {
}
}
}
</style>
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div class="welcome">
<div class="container">
<h1 class="title">{{ title }}-{{ version }}</h1>
<h2 class="title">{{ title }}-{{ version }}</h2>
<el-row :gutter="20">
<el-col :span="8">
@@ -128,10 +128,11 @@ const send = (text) => {
width 100%
.title {
font-size: 2.25rem
// font-size: 2.25rem
line-height: 2.5rem
font-weight 600
margin-bottom: 4rem
color var( --theme-textcolor-normal)
}
.grid-content {

View File

@@ -1,57 +1,58 @@
<template>
<div class="black-input-wrapper">
<el-input v-model="model" :type="type" :rows="rows"
@input="onInput"
style="--el-input-bg-color:#252020;
--el-input-border-color:#414141;
--el-input-focus-border-color:#414141;
--el-text-color-regular: #f1f1f1;
--el-input-border-radius: 10px;
--el-border-color-hover:#616161"
resize="none"
:placeholder="placeholder" :maxlength="maxlength"/>
<el-input
v-model="model"
:type="type"
:rows="rows"
@input="onInput"
resize="none"
:placeholder="placeholder"
:maxlength="maxlength"
/>
<div class="word-stat" v-if="rows > 1">
<span>{{value.length}}</span>/<span>{{maxlength}}</span>
<span>{{ value.length }}</span
>/<span>{{ maxlength }}</span>
</div>
</div>
</template>
<script setup>
import {ref, watch} from "vue";
import { ref, watch } from "vue";
const props = defineProps({
value : {
value: {
type: String,
default: '',
default: ""
},
placeholder: {
type: String,
default: '',
default: ""
},
type: {
type: String,
default: 'input',
default: "input"
},
rows: {
type: Number,
default: 5,
default: 5
},
maxlength: {
type: Number,
default: 1024
}
});
watch(() => props.value, (newValue) => {
model.value = newValue
})
const model = ref(props.value)
watch(
() => props.value,
(newValue) => {
model.value = newValue;
}
);
const model = ref(props.value);
// eslint-disable-next-line no-undef
const emits = defineEmits(['update:value']);
const emits = defineEmits(["update:value"]);
const onInput = (value) => {
emits('update:value',value)
}
emits("update:value", value);
};
</script>
<style lang="stylus">
@@ -77,4 +78,4 @@ const onInput = (value) => {
}
}
}
</style>
</style>

View File

@@ -1,33 +1,32 @@
<template>
<el-select v-model="model" :placeholder="placeholder"
:value="value" @change="$emit('update:value', $event)"
style="--el-fill-color-blank:#252020;
--el-text-color-regular: #a1a1a1;
--el-select-disabled-color:#0E0808;
--el-color-primary-light-9:#0E0808;
--el-border-radius-base:20px;
--el-border-color:#0E0808;">
<el-option v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
<el-select
v-model="model"
:placeholder="placeholder"
:value="value"
@change="$emit('update:value', $event)"
style="--el-border-radius-base: 20px"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</template>
<script>
export default {
name: 'BlackSelect',
name: "BlackSelect",
props: {
value : {
value: {
type: String,
default: '',
default: ""
},
placeholder: {
type: String,
default: '请选择',
default: "请选择"
},
options: {
type: Array,
@@ -37,7 +36,7 @@ export default {
data() {
return {
model: this.value
}
};
}
}
};
</script>

View File

@@ -1,24 +1,26 @@
<template>
<el-switch v-model="model" :size="size"
@change="$emit('update:value', $event)"
style="--el-switch-on-color:#555555;--el-color-white:#0E0808"/>
<el-switch
v-model="model"
:size="size"
@change="$emit('update:value', $event)"
/>
</template>
<script setup>
import {ref, watch} from "vue";
import { ref, watch } from "vue";
const props = defineProps({
value : Boolean,
value: Boolean,
size: {
type: String,
default: 'default',
default: "default"
}
});
const model = ref(props.value)
const model = ref(props.value);
watch(() => props.value, (newValue) => {
model.value = newValue
})
watch(
() => props.value,
(newValue) => {
model.value = newValue;
}
);
</script>