💄 调整UI

This commit is contained in:
BTMuli
2025-12-19 01:01:22 +08:00
parent 3c4ff261fd
commit 11582330b5
14 changed files with 288 additions and 209 deletions

View File

@@ -2,14 +2,14 @@
<div
v-if="card"
:id="`post-card-${card.postId}`"
class="tpc-card"
:class="{ 'select-mode': props.selectMode }"
class="tpc-card"
@click="trySelect()"
>
<div class="tpc-top">
<div class="tpc-cover" @click="toPost()">
<TMiImg :src="card.cover" alt="cover" :ori="true" v-if="card.cover !== ''" />
<img src="/source/UI/defaultCover.webp" alt="cover" v-else />
<TMiImg v-if="card.cover !== ''" :ori="true" :src="card.cover" alt="cover" />
<img v-else alt="cover" src="/source/UI/defaultCover.webp" />
<div v-if="card.status" class="tpc-act">
<div class="tpc-status">{{ card.status?.label }}</div>
<div class="tpc-time">
@@ -19,20 +19,20 @@
</div>
<div
v-else-if="props.modelValue.post.images.length > 1"
class="tpc-image-cnt"
:title="`图片数:${props.modelValue.post.images.length}`"
class="tpc-image-cnt"
>
<v-icon size="10">mdi-folder-multiple-image</v-icon>
<span>{{ props.modelValue.post.images.length }}</span>
</div>
</div>
<div class="tpc-title" :title="card.title" @click="shareCard()">{{ card.title }}</div>
<div :title="card.title" class="tpc-title" @click="shareCard()">{{ card.title }}</div>
</div>
<div class="tpc-mid" v-if="card.user !== null">
<div v-if="card.user !== null" class="tpc-mid">
<TpAvatar
:data="card.user"
position="left"
:style="{ cursor: props.userClick ? 'pointer' : 'default' }"
position="left"
@click="onUserClick()"
/>
</div>
@@ -45,41 +45,41 @@
<TpcTag
v-for="topic in card.topics"
:key="topic.id"
@click="toTopic(topic)"
:tag="topic.name"
@click="toTopic(topic)"
/>
</div>
<div class="tpc-data" v-if="card.data !== null">
<div class="tpc-info-item" :title="`浏览数:${card.data.view}`">
<div v-if="card.data !== null" class="tpc-data">
<div :title="`浏览数:${card.data.view}`" class="tpc-info-item">
<v-icon size="12">mdi-eye</v-icon>
<span>{{ card.data.view }}</span>
</div>
<div class="tpc-info-item" :title="`收藏数:${card.data.mark}`">
<div :title="`收藏数:${card.data.mark}`" class="tpc-info-item">
<v-icon size="12">mdi-star</v-icon>
<span>{{ card.data.mark }}</span>
</div>
<div class="tpc-info-item" :title="`回复数:${card.data.reply}`">
<div :title="`回复数:${card.data.reply}`" class="tpc-info-item">
<v-icon size="12">mdi-comment</v-icon>
<span>{{ card.data.reply }}</span>
</div>
<div class="tpc-info-item" :title="`点赞数:${card.data.like}`">
<div :title="`点赞数:${card.data.like}`" class="tpc-info-item">
<v-icon size="12">mdi-thumb-up</v-icon>
<span>{{ card.data.like }}</span>
</div>
<div class="tpc-info-item" :title="`转发数:${card.data.forward}`">
<div :title="`转发数:${card.data.forward}`" class="tpc-info-item">
<v-icon size="12">mdi-share-variant</v-icon>
<span>{{ card.data.forward }}</span>
</div>
</div>
<div class="tpc-data">
<div class="tpc-info-item" :title="`创建时间: ${card.meta.create_time}`">
<div :title="`创建时间: ${card.meta.create_time}`" class="tpc-info-item">
<v-icon size="12">mdi-calendar-clock</v-icon>
<span>{{ card.meta.create_time }}</span>
</div>
<div
v-if="card.meta.update_time"
class="tpc-info-item"
:title="`更新时间: ${card.meta.update_time}`"
class="tpc-info-item"
>
<v-icon size="12">mdi-calendar-edit</v-icon>
<span>{{ card.meta.update_time }}</span>
@@ -87,30 +87,30 @@
</div>
</div>
<div
class="tpc-forum"
v-if="card.forum !== null && card.forum.name !== ''"
:style="{
background: str2Color(`${card.forum.id}${card.forum.name}`, -60),
}"
v-if="card.forum !== null && card.forum.name !== ''"
:title="`频道: ${card.forum.name}`"
class="tpc-forum"
@click="toForum(card.forum)"
>
<img v-if="card.forum.icon !== ''" :src="card.forum.icon" :alt="card.forum.name" />
<img v-if="card.forum.icon !== ''" :alt="card.forum.name" :src="card.forum.icon" />
<span>{{ card.forum.name }}</span>
</div>
<v-checkbox-btn
v-model="isSelected"
v-if="props.selectMode"
v-model="isSelected"
class="tpc-select"
@click.stop="trySelect()"
data-html2canvas-ignore
@click.stop="trySelect()"
/>
<div
class="tpc-info-id"
v-else
:style="{
background: str2Color(`${props.modelValue.post.post_id}`, 0),
}"
v-else
class="tpc-info-id"
>
<span>{{ props.modelValue.post.post_id }}</span>
<template v-if="isDevEnv">
@@ -125,9 +125,10 @@ import showSnackbar from "@comp/func/snackbar.js";
import TpAvatar from "@comp/viewPost/tp-avatar.vue";
import TpcTag from "@comp/viewPost/tpc-tag.vue";
import { emit } from "@tauri-apps/api/event";
import { str2Color } from "@utils/colorFunc.js";
import { generateShareImg } from "@utils/TGShare.js";
import { createPost } from "@utils/TGWindow.js";
import { str2Color, timestampToDate } from "@utils/toolFunc.js";
import { timestampToDate } from "@utils/toolFunc.js";
import { computed, onMounted, ref, shallowRef, watch } from "vue";
import { useRoute, useRouter } from "vue-router";

View File

@@ -14,14 +14,14 @@
{{ parseTitle(model.subtitle) }}
</div>
<div
:style="{ background: str2Color(`${model.tagIcon}${model.tagLabel}`, 40) }"
:title="`标签:${model.tagLabel}`"
class="anno-label"
:style="{ background: str2Color(`${model.tagIcon}${model.tagLabel}`, 40) }"
>
<TMiImg :src="model.tagIcon" alt="tag" :ori="true" />
<TMiImg :ori="true" :src="model.tagIcon" alt="tag" />
<span>{{ model.tagLabel }}</span>
</div>
<div class="anno-id" :style="{ background: str2Color(`${model.id}`, 0) }">
<div :style="{ background: str2Color(`${model.id}`, 0) }" class="anno-id">
ID:{{ model.id }}
</div>
</div>
@@ -31,10 +31,11 @@ import TMiImg from "@comp/app/t-mi-img.vue";
import showSnackbar from "@comp/func/snackbar.js";
import { AnnoTypeEnum } from "@enum/anno.js";
import useAppStore from "@store/app.js";
import { str2Color } from "@utils/colorFunc.js";
import TGLogger from "@utils/TGLogger.js";
import { generateShareImg } from "@utils/TGShare.js";
import { createTGWindow } from "@utils/TGWindow.js";
import { decodeRegExp, str2Color } from "@utils/toolFunc.js";
import { decodeRegExp } from "@utils/toolFunc.js";
import { storeToRefs } from "pinia";
import { onMounted, ref, watch } from "vue";

View File

@@ -2,16 +2,17 @@
<template>
<div :title="props.info.name" class="pb-mi-box" @click="toMaterial()">
<div class="pb-mi-left">
<img :src="`/icon/bg/${props.info.star}-Star.webp`" alt="bg" class="pb-mi-bg" />
<img :src="`/icon/material/${props.info.id}.webp`" alt="icon" class="pb-mi-icon" />
<img :src="`/icon/bg/${props.info.star}-Star.webp`" alt="bg" class="bg" />
<img :src="`/icon/material/${props.info.id}.webp`" alt="icon" class="icon" />
</div>
<div class="pb-mi-right">{{ props.info.name }}</div>
<div class="pb-mi-id">{{ props.info.id }}</div>
<div class="pb-mi-id">{{ props.info.type }}·{{ props.info.id }}</div>
<div class="pb-mi-cnt">{{ item.count }}</div>
</div>
</template>
<script lang="ts" setup>
import { shallowRef, watch } from "vue";
import { getOdStarColor } from "@utils/colorFunc.js";
import { computed, shallowRef, watch } from "vue";
import type { MaterialInfo } from "@/pages/common/PageBagMaterial.vue";
@@ -44,6 +45,8 @@ watch(
}
},
);
const idColor = computed<string>(() => getOdStarColor(props.info.star));
</script>
<style lang="scss" scoped>
@use "@styles/github.styles.scss" as github-styles;
@@ -51,39 +54,37 @@ watch(
.pb-mi-box {
position: relative;
display: flex;
height: 45px;
overflow: hidden;
height: 48px;
align-items: center;
justify-content: flex-start;
padding-right: 5px;
padding-right: 8px;
border: 1px solid var(--common-shadow-1);
border-radius: 5px;
border-radius: 4px;
background: var(--box-bg-1);
column-gap: 5px;
column-gap: 4px;
cursor: pointer;
}
.pb-mi-left {
position: relative;
width: 45px;
height: 45px;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
}
height: 100%;
flex-shrink: 0;
aspect-ratio: 1;
.pb-mi-bg,
.pb-mi-icon {
position: absolute;
top: 0;
width: 45px;
height: 45px;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
.bg,
.icon {
position: absolute;
top: 0;
width: 100%;
height: 100%;
}
}
.pb-mi-right {
position: relative;
overflow: hidden;
max-width: calc(100% - 50px);
max-width: 100%;
color: var(--box-text-2);
font-size: 14px;
text-overflow: ellipsis;
@@ -93,26 +94,30 @@ watch(
.pb-mi-id {
position: absolute;
right: 4px;
bottom: 2px;
z-index: 1;
right: 2px;
bottom: 0;
color: v-bind(idColor); /* stylelint-disable-line value-keyword-case */
font-size: 8px;
opacity: 0.6;
font-style: italic;
opacity: 0.8;
}
.pb-mi-cnt {
@include github-styles.github-tag-dark-gen(#82aaff);
@include github-styles.github-tag-dark-gen(#009688);
position: absolute;
top: 0;
right: 0;
box-sizing: border-box;
padding-right: 4px;
padding-left: 12px;
border-top: unset;
border-left: unset;
border-bottom-left-radius: 20px;
border-top-right-radius: 4px;
border-right: unset;
border-bottom-left-radius: 12px;
font-family: var(--font-title);
font-size: 10px;
line-height: 12px;
text-align: center;
}
</style>

View File

@@ -41,6 +41,7 @@
<span class="cnt">{{ record.count }}</span>
<span class="type">{{ record.manual ? "手动更新" : "自动导入" }}</span>
</div>
<div v-if="dbInfo.records.length === 0">暂无记录</div>
</div>
</div>
</div>

View File

@@ -1,72 +1,144 @@
<!-- 角色/武器WIKI侧边栏项 -->
<template>
<div class="twc-li-box">
<div :class="{ selected: props.curItem.id === props.data.id }" class="twc-li-box">
<div class="twc-li-left">
<!-- TODO: 角色添加元素&武器类型武器添加类型 -->
<img class="twc-li-bg" :src="`/icon/bg/${props.data.star}-Star.webp`" alt="bg" />
<img class="twc-li-icon" :src="`/WIKI/${props.mode}/${props.data.id}.webp`" alt="icon" />
<img :src="`/icon/bg/${props.data.star}-Star.webp`" alt="bg" class="bg" />
<img :src="`/WIKI/${props.mode}/${props.data.id}.webp`" alt="icon" class="icon" />
</div>
<div :title="props.data.name" class="twc-li-right">{{ props.data.name }}</div>
<div
:title="`${props.mode === 'weapon' ? '武器' : '角色'}ID:${props.data.id}`"
class="twc-li-id"
>
{{ props.data.id }}
</div>
<div class="twc-li-icons">
<template v-if="props.mode === 'character'">
<img
:src="`/icon/element/${props.data.element}元素.webp`"
:title="`${props.data.element}元素`"
alt="element"
class="element"
/>
</template>
<img
:src="`/icon/weapon/${props.data.weapon}.webp`"
:title="props.data.weapon"
alt="weapon"
class="weapon"
/>
</div>
<div class="twc-li-right" :title="props.data.name">{{ props.data.name }}</div>
</div>
</template>
<script lang="ts" setup>
type TwcListItemProps =
| {
mode: "character";
data: TGApp.App.Character.WikiBriefInfo;
curItem: TGApp.App.Character.WikiBriefInfo;
}
| {
mode: "weapon";
data: TGApp.App.Weapon.WikiBriefInfo;
curItem: TGApp.App.Weapon.WikiBriefInfo;
};
import { getOdStarColor } from "@utils/colorFunc.js";
import { computed } from "vue";
/** 角色数据 */
type TwcListItemAvatar = {
mode: "character";
data: TGApp.App.Character.WikiBriefInfo;
curItem: TGApp.App.Character.WikiBriefInfo;
};
/** 武器数据 */
type TwcListItemWeapon = {
mode: "weapon";
data: TGApp.App.Weapon.WikiBriefInfo;
curItem: TGApp.App.Weapon.WikiBriefInfo;
};
type TwcListItemProps = (TwcListItemAvatar | TwcListItemWeapon) & {
width?: number;
};
const props = defineProps<TwcListItemProps>();
const idColor = computed<string>(() => getOdStarColor(props.data.star));
</script>
<style lang="scss" scoped>
.twc-li-box {
position: relative;
display: flex;
height: 45px;
height: 40px;
box-sizing: border-box;
align-items: center;
border: 1px solid var(--common-shadow-1);
border-radius: 5px;
background: v-bind("props.data.id === props.curItem.id ? 'var(--box-bg-2)' : 'var(--box-bg-1)'");
border-radius: 4px;
background: var(--box-bg-1);
cursor: pointer;
gap: 10px;
gap: 4px;
&.selected {
background: var(--box-bg-2);
}
}
.twc-li-left {
width: 45px;
height: 45px;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
}
.twc-li-bg,
.twc-li-icon {
position: absolute;
top: 0;
width: 45px;
height: 45px;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
}
.twc-li-bg img,
.twc-li-icon img {
width: 100%;
position: relative;
height: 100%;
border-bottom-left-radius: 5px;
border-top-left-radius: 5px;
object-fit: cover;
flex-shrink: 0;
aspect-ratio: 1;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
.bg,
.icon {
position: absolute;
top: 0;
width: 100%;
height: 100%;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
img {
width: 100%;
height: 100%;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
object-fit: cover;
}
}
.bg {
z-index: 0;
}
.icon {
z-index: 1;
}
}
.twc-li-right {
overflow: hidden;
padding-right: 8px;
color: var(--app-page-content);
font-size: 14px;
text-overflow: ellipsis;
white-space: nowrap;
}
.twc-li-id {
position: absolute;
z-index: 1;
right: 2px;
bottom: 0;
color: v-bind(idColor); /* stylelint-disable-line value-keyword-case */
font-size: 6px;
font-style: italic;
opacity: 0.8;
}
.twc-li-icons {
position: absolute;
z-index: 1;
top: 2px;
right: 2px;
display: flex;
align-items: center;
justify-content: center;
img {
width: 16px;
height: 16px;
}
}
</style>

View File

@@ -1,4 +1,4 @@
<!-- 游戏签到 TODO: 补签 -->
<!-- 游戏签到 -->
<template>
<div class="tuss-box">
<div class="tuss-top">

View File

@@ -1,22 +1,22 @@
<template>
<span
v-if="mode == 'link'"
:style="getTextStyle()"
:title="props.data.attributes?.link"
class="tp-text-link"
@click="toLink()"
@contextmenu="copyLink()"
:title="props.data.attributes?.link"
:style="getTextStyle()"
>
{{ decodeRegExp(props.data.insert) }}
</span>
<span v-else-if="mode == 'emoji'" class="tp-text-emoji">
<img :src="getEmojiUrl()" :alt="getEmojiName()" :title="getEmojiName()" />
<img :alt="getEmojiName()" :src="getEmojiUrl()" :title="getEmojiName()" />
</span>
<TpText
v-else-if="mode == 'emojis'"
v-for="(emoji, indexE) in emojis"
:data="emoji"
v-else-if="mode == 'emojis'"
:key="indexE"
:data="emoji"
/>
<span v-else :style="getTextStyle()">{{ decodeRegExp(props.data.insert) }}</span>
</template>
@@ -24,8 +24,9 @@
import showSnackbar from "@comp/func/snackbar.js";
import bbsReq from "@req/bbsReq.js";
import { openUrl } from "@tauri-apps/plugin-opener";
import { isColorSimilar } from "@utils/colorFunc.js";
import { parseLink, parsePost } from "@utils/linkParser.js";
import { isColorSimilar, decodeRegExp } from "@utils/toolFunc.js";
import { decodeRegExp } from "@utils/toolFunc.js";
import { onMounted, ref, shallowRef, StyleValue, toRaw } from "vue";
export type TpText = {

View File

@@ -6,7 +6,7 @@
</template>
<script lang="ts" setup>
import useAppStore from "@store/app.js";
import { str2Color } from "@utils/toolFunc.js";
import { str2Color } from "@utils/colorFunc.js";
import { storeToRefs } from "pinia";
import { computed } from "vue";

View File

@@ -1,11 +1,11 @@
<template>
<span class="tag-label" :title="`点击跳转#${props.tag}#`">
<span :title="`点击跳转#${props.tag}#`" class="tag-label">
{{ props.tag }}
</span>
</template>
<script lang="ts" setup>
import useAppStore from "@store/app.js";
import { str2Color } from "@utils/toolFunc.js";
import { str2Color } from "@utils/colorFunc.js";
import { storeToRefs } from "pinia";
import { computed } from "vue";

View File

@@ -278,12 +278,12 @@ async function refresh(): Promise<void> {
return;
}
}
await TGLogger.Info(`[Character][refreshRoles][${account.value.gameUid}] 正在更新角色数据`);
if (!cookie.value) {
showSnackbar.warn("请先登录");
loadData.value = false;
return;
}
await TGLogger.Info(`[Character][refreshRoles][${account.value.gameUid}] 正在更新角色数据`);
await showLoading.start(`正在更新${account.value.gameUid}的角色数据`);
loadData.value = true;
await showLoading.update("正在刷新首页数据");
@@ -308,7 +308,7 @@ async function refresh(): Promise<void> {
return;
}
const idList = listRes.map((i) => i.id.toString());
await showLoading.update(`${idList.length}个角色`);
await showLoading.update(`${idList.length}个角色,正在获取角色详情`);
const res = await recordReq.character.detail(cookie.value, account.value, idList);
if ("retcode" in res) {
showSnackbar.error(`[${res.retcode}] ${res.message}`);

View File

@@ -12,6 +12,7 @@
:key="index"
v-model:cur-item="curItem"
:data="item"
:width="160"
mode="character"
@click="switchC(item)"
/>
@@ -128,30 +129,31 @@ async function toOuter(item?: TGApp.App.Character.WikiBriefInfo): Promise<void>
.wc-box {
position: relative;
display: flex;
height: calc(100vh - 40px);
column-gap: 10px;
max-height: calc(100vh - 32px);
column-gap: 8px;
}
.wc-left {
display: flex;
width: 500px;
width: fit-content;
flex-direction: column;
gap: 10px;
flex-shrink: 0;
gap: 8px;
}
.wc-select {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 10px;
gap: 8px;
}
.wc-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
border-radius: 5px;
padding: 8px;
border-radius: 4px;
background: var(--tgc-btn-1);
color: var(--btn-text);
}
@@ -159,21 +161,20 @@ async function toOuter(item?: TGApp.App.Character.WikiBriefInfo): Promise<void>
.wc-list {
position: relative;
display: grid;
width: 500px;
height: calc(100% - 40px);
padding-right: 10px;
gap: 10px;
grid-auto-rows: 45px;
grid-template-columns: repeat(3, minmax(100px, 1fr));
width: 100%;
padding-right: 8px;
gap: 8px;
grid-template-columns: repeat(3, 160px);
overflow-y: auto;
}
.wc-detail {
max-height: 100%;
flex: 1;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px var(--common-shadow-2);
position: relative;
width: 100%;
box-sizing: border-box;
padding: 8px;
border-radius: 4px;
box-shadow: 0 0 4px var(--common-shadow-2);
overflow-y: auto;
}
</style>

View File

@@ -99,34 +99,35 @@ async function toOuter(item?: TGApp.App.Weapon.WikiBriefInfo): Promise<void> {
await toObcPage(item.contentId);
}
</script>
<style lang="css" scoped>
<style lang="scss" scoped>
.ww-box {
position: relative;
display: flex;
max-height: calc(100vh - 40px);
column-gap: 10px;
max-height: calc(100vh - 32px);
column-gap: 8px;
}
.ww-left {
display: flex;
width: 500px;
width: fit-content;
flex-direction: column;
gap: 10px;
flex-shrink: 0;
gap: 8px;
}
.ww-select {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 10px;
gap: 8px;
}
.ww-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
border-radius: 5px;
padding: 8px;
border-radius: 4px;
background: var(--tgc-btn-1);
color: var(--btn-text);
}
@@ -134,24 +135,20 @@ async function toOuter(item?: TGApp.App.Weapon.WikiBriefInfo): Promise<void> {
.ww-list {
position: relative;
display: grid;
width: 500px;
height: calc(100% - 40px);
padding-right: 10px;
gap: 10px;
grid-auto-rows: 45px;
grid-template-columns: repeat(3, minmax(100px, 1fr));
width: 100%;
padding-right: 8px;
gap: 8px;
grid-template-columns: repeat(3, 160px);
overflow-y: auto;
}
.ww-detail {
position: relative;
height: calc(100vh - 40px);
max-height: 100%;
width: 100%;
box-sizing: border-box;
flex: 1;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px var(--common-shadow-2);
padding: 8px;
border-radius: 4px;
box-shadow: 0 0 4px var(--common-shadow-2);
overflow-y: auto;
}
</style>

60
src/utils/colorFunc.ts Normal file
View File

@@ -0,0 +1,60 @@
/**
* 颜色相关处理
* @since Beta v0.9.0
*/
import { score } from "wcag-color";
/**
* 根据传入星级获取对应颜色
* @since Beta v0.9.0
* @param {number} star
* @returns {string} color
*/
export function getOdStarColor(star: number): string {
switch (star) {
case 5:
return "var(--tgc-od-orange)";
case 4:
return "var(--tgc-od-purple)";
case 3:
return "var(--tgc-od-blue)";
case 2:
return "var(--tgc-od-green)";
case 1:
return "var(--tgc-od-white)";
default:
return "var(--tgc-od-red)";
}
}
/**
* 判断颜色是否相似
* @since Beta v0.9.0
* @param {string} colorBg - 背景颜色
* @param {string} colorText - 文本颜色
* @returns {boolean} 是否相似
*/
export function isColorSimilar(colorBg: string, colorText: string): boolean {
return score(colorText, colorBg) === "Fail";
}
/**
* @description 根据字符串生成颜色
* @since Beta v0.8.2
* @param {string} str - 输入字符串
* @param {number} adjust - 亮度调整值,正数变亮,负数变暗
* @returns {string} 生成的颜色 rgb(r, g, b)
*/
export function str2Color(str: string, adjust: number): string {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
let r = (hash >> 16) & 0xff;
let g = (hash >> 8) & 0xff;
let b = hash & 0xff;
r = Math.min(Math.max(r + adjust, 0), 255);
g = Math.min(Math.max(g + adjust, 0), 255);
b = Math.min(Math.max(b + adjust, 0), 255);
return `rgb(${r}, ${g}, ${b})`;
}

View File

@@ -1,14 +1,12 @@
/**
* 一些工具函数
* @since Beta v0.9.0
*/
import { AvatarExtResTypeEnum, AvatarExtTypeEnum } from "@enum/bbs.js";
import { path } from "@tauri-apps/api";
import { type } from "@tauri-apps/plugin-os";
import colorConvert from "color-convert";
import type { KEYWORD } from "color-convert/conversions.js";
import { v4 } from "uuid";
import { score } from "wcag-color";
import { AppCharacterData, AppWeaponData } from "@/data/index.js";
@@ -179,43 +177,6 @@ export function getRandomString(length: number, type: string = "all"): string {
return res;
}
/**
* @description 将颜色转为 hex
* @since Beta v0.3.9
* @param {string} color - 颜色
* @returns {string} hex
*/
function color2Hex(color: string): string {
if (color.startsWith("#")) return color;
if (color.startsWith("rgb")) {
// 正则获取 rgb(0, 0, 0) 或 rgba(0, 0, 0, 0) 或 rgb(0 0 0/0)
const reg = /rgba?\((.*?)\)/;
const match = reg.exec(color);
if (match === null) return "#000000";
const rgb = match[1];
const rgbArr = rgb.split(/[ ,/]/);
if (rgbArr.length < 3) return "#000000";
const r = parseInt(rgbArr[0]);
const g = parseInt(rgbArr[1]);
const b = parseInt(rgbArr[2]);
return colorConvert.rgb.hex([r, g, b]);
}
return colorConvert.keyword.hex(<KEYWORD>color);
}
/**
* @description 判断颜色是否相似
* @since Beta v0.3.9
* @param {string} colorBg - 背景颜色
* @param {string} colorText - 文本颜色
* @returns {boolean} 是否相似
*/
export function isColorSimilar(colorBg: string, colorText: string): boolean {
const hexBg = color2Hex(colorBg);
const hexText = color2Hex(colorText);
return score(hexText, hexBg) === "Fail";
}
/**
* @description 解析带样式的文本
* @since Beta v0.8.1
@@ -357,24 +318,3 @@ export function getUserAvatar(
// TODO: 处理其他类型头像
return user.avatar_url;
}
/**
* @description 根据字符串生成颜色
* @since Beta v0.8.2
* @param {string} str - 输入字符串
* @param {number} adjust - 亮度调整值,正数变亮,负数变暗
* @returns {string} 生成的颜色 rgb(r, g, b)
*/
export function str2Color(str: string, adjust: number): string {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
let r = (hash >> 16) & 0xff;
let g = (hash >> 8) & 0xff;
let b = hash & 0xff;
r = Math.min(Math.max(r + adjust, 0), 255);
g = Math.min(Math.max(g + adjust, 0), 255);
b = Math.min(Math.max(b + adjust, 0), 255);
return `rgb(${r}, ${g}, ${b})`;
}