状态栏

This commit is contained in:
unanmed 2022-12-28 20:34:23 +08:00
parent 0ecdcbdf34
commit 22350c4dda
24 changed files with 673 additions and 49 deletions

2
components.d.ts vendored
View File

@ -7,10 +7,12 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
AButton: typeof import('ant-design-vue/es')['Button']
ADivider: typeof import('ant-design-vue/es')['Divider']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASlider: typeof import('ant-design-vue/es')['Slider']
Box: typeof import('./src/components/box.vue')['default']
BoxAnimate: typeof import('./src/components/boxAnimate.vue')['default']
EnemyOne: typeof import('./src/components/enemyOne.vue')['default']
Scroll: typeof import('./src/components/scroll.vue')['default']

View File

@ -193,6 +193,7 @@
</div>
</div>
<div id="root"></div>
<div id="root2"></div>
<!-- injection -->
<script src='libs/thirdparty/lz-string.min.js'></script>
<script src='libs/thirdparty/priority-queue.min.js'></script>

View File

@ -888,7 +888,11 @@ actions.prototype._getClickLoc = function (x, y) {
if (core.domStyle.isVertical) {
statusBar.x = 3;
statusBar.y = core.dom.statusBar.offsetHeight + 3;
statusBar.y =
core.dom.statusBar.offsetHeight +
3 +
32 * core.values.statusCanvasRowsOnMobile +
9;
} else {
statusBar.x = core.dom.statusBar.offsetWidth + 3;
statusBar.y = 3;

View File

@ -4274,7 +4274,10 @@ control.prototype._resize_gameGroup = function (obj) {
gameGroup.style.width = totalWidth + 'px';
gameGroup.style.height = totalHeight + 'px';
gameGroup.style.left = (obj.clientWidth - totalWidth) / 2 + 'px';
gameGroup.style.top = (obj.clientHeight - totalHeight) / 2 + 'px';
gameGroup.style.top =
(obj.clientHeight - totalHeight) / 2 +
(core.domStyle.isVertical ? totalHeight / 8 : 0) +
'px';
// floorMsgGroup
var floorMsgGroup = core.dom.floorMsgGroup;
floorMsgGroup.style = obj.globalAttribute.floorChangingStyle;
@ -4437,8 +4440,8 @@ control.prototype._resize_statusBar = function (obj) {
(obj.extendToolbar ? obj.TOOLBAR_HEIGHT + obj.BORDER : 0)
);
}
core.dom.statusCanvas.style.display =
core.flags.statusCanvas && !obj.extendToolbar ? 'block' : 'none';
core.dom.statusCanvas.style.display = 'none';
statusBar.style.display = 'none';
};
control.prototype._resize_status = function (obj) {

View File

@ -616,7 +616,6 @@ main.prototype.listen = function () {
////// 鼠标移动时 //////
main.dom.data.onmousemove = function (e) {
try {
e.stopPropagation();
var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
if (loc == null) return;
main.core.onmove(loc);
@ -628,7 +627,6 @@ main.prototype.listen = function () {
////// 鼠标放开时 //////
main.dom.data.onmouseup = function (e) {
try {
e.stopPropagation();
var loc = main.core.actions._getClickLoc(e.clientX, e.clientY);
if (loc == null) return;
main.core.onup(loc);

View File

@ -62,6 +62,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"money.png",
"mount.jpg",
"plot1.jpg",
"skill.png",
"skill0.png",
"skill1.png",
"skill2.png",
@ -684,7 +685,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"enableRouteFolding": false,
"disableShopOnDamage": false,
"blurFg": true,
"extendToolbar": false,
"extendToolbar": true,
"enableEnemyPoint": null
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -9289,5 +9289,35 @@ var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = {
core.plugin.statusBarStatus.value =
!core.plugin.statusBarStatus.value;
};
control.prototype.showStatusBar = function () {
if (main.mode == 'editor') return;
core.removeFlag('hideStatusBar');
core.plugin.showStatusBar.value = true;
core.dom.tools.hard.style.display = 'block';
core.dom.toolBar.style.display = 'block';
};
control.prototype.hideStatusBar = function (showToolbox) {
if (main.mode == 'editor') return;
// 如果原本就是隐藏的,则先显示
if (!core.domStyle.showStatusBar) this.showStatusBar();
if (core.isReplaying()) showToolbox = true;
core.plugin.showStatusBar.value = false;
core.setFlag('hideStatusBar', true);
core.setFlag('showToolbox', showToolbox || null);
if (
(!core.domStyle.isVertical && !core.flags.extendToolbar) ||
!showToolbox
) {
for (var i = 0; i < toolItems.length; ++i)
toolItems[i].style.display = 'none';
}
if (!core.domStyle.isVertical && !core.flags.extendToolbar) {
core.dom.toolBar.style.display = 'none';
}
};
}
};

19
src/App2.vue Normal file
View File

@ -0,0 +1,19 @@
<template>
<div id="non-ui">
<StatusBar v-if="showStatusBar"></StatusBar>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import StatusBar from './ui/statusBar.vue';
import { showStatusBar } from './plugin/uiController';
</script>
<style lang="less" scoped>
#non-ui {
width: 0;
height: 0;
overflow: visible;
}
</style>

313
src/components/box.vue Normal file
View File

@ -0,0 +1,313 @@
<template>
<div :id="`box-${id}`" class="box">
<div :id="`box-main-${id}`" class="box-main" @click="click">
<slot></slot>
</div>
<div :id="`box-move-${id}`" class="box-move" :selected="moveSelected">
<drag-outlined
:id="`box-drag-${id}`"
class="box-drag"
style="right: 0; bottom: 0; position: absolute"
/>
</div>
<div
class="border border-vertical border-left"
:id="`border-left-${id}`"
:selected="moveSelected && resizable"
></div>
<div
class="border border-horizontal border-top"
:id="`border-top-${id}`"
:selected="moveSelected && resizable"
></div>
<div
class="border border-vertical border-right"
:id="`border-right-${id}`"
:selected="moveSelected && resizable"
></div>
<div
class="border border-horizontal border-bottom"
:id="`border-bottom-${id}`"
:selected="moveSelected && resizable"
></div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, onUpdated, ref, watch } from 'vue';
import { DragOutlined } from '@ant-design/icons-vue';
import { isMobile, useDrag, cancelGlobalDrag } from '../plugin/use';
import { has } from '../plugin/utils';
const props = defineProps<{
resizable?: boolean;
left?: number;
top?: number;
width?: number;
height?: number;
}>();
const emits = defineEmits<{
(e: 'update:left', data: number): void;
(e: 'update:top', data: number): void;
(e: 'update:width', data: number): void;
(e: 'update:height', data: number): void;
}>();
const id = (1e8 * Math.random()).toFixed(0);
const moveSelected = ref(false);
let moveTimeout = 0;
let move: HTMLDivElement;
let main: HTMLDivElement;
let leftB: HTMLDivElement;
let rightB: HTMLDivElement;
let topB: HTMLDivElement;
let bottomB: HTMLDivElement;
let drag: HTMLElement;
const width = ref(isMobile ? window.innerWidth - 100 : 300);
const height = ref(isMobile ? 250 : window.innerHeight - 100);
const left = ref(50);
const top = ref(50);
watch(left, n => emits('update:left', n));
watch(top, n => emits('update:top', n));
watch(width, n => emits('update:width', n));
watch(height, n => emits('update:height', n));
async function click() {
moveSelected.value = true;
moveTimeout = window.setTimeout(() => {
moveSelected.value = false;
}, 4000);
}
let lastX = 0;
let lastY = 0;
function dragFn(x: number, y: number) {
const style = getComputedStyle(main);
const ox = parseFloat(style.left);
const oy = parseFloat(style.top);
left.value = ox + x - lastX;
top.value = oy + y - lastY;
main.style.left = `${left.value}px`;
main.style.top = `${top.value}px`;
moveSelected.value = true;
clearTimeout(moveTimeout);
lastX = x;
lastY = y;
}
let right = left.value + width.value;
function leftDrag(x: number, y: number) {
main.style.left = `${x}px`;
width.value = right - x;
left.value = x;
main.style.width = `${width.value}px`;
}
let bottom = top.value + height.value;
function topDrag(x: number, y: number) {
main.style.top = `${y}px`;
height.value = bottom - y;
top.value = y;
main.style.height = `${height.value}px`;
}
function rightDrag(x: number, y: number) {
const style = getComputedStyle(main);
width.value = x - parseFloat(style.left);
main.style.width = `${width.value}px`;
}
function bottomDrag(x: number, y: number) {
const style = getComputedStyle(main);
height.value = y - parseFloat(style.top);
main.style.height = `${height.value}px`;
}
function resize() {
move = document.getElementById(`box-move-${id}`) as HTMLDivElement;
main = document.getElementById(`box-${id}`) as HTMLDivElement;
leftB = document.getElementById(`border-left-${id}`) as HTMLDivElement;
topB = document.getElementById(`border-top-${id}`) as HTMLDivElement;
rightB = document.getElementById(`border-right-${id}`) as HTMLDivElement;
bottomB = document.getElementById(`border-bottom-${id}`) as HTMLDivElement;
drag = document.getElementById(`box-drag-${id}`) as HTMLElement;
if (has(props.left)) left.value = props.left;
if (has(props.top)) top.value = props.top;
if (has(props.width)) width.value = props.width;
if (has(props.height)) height.value = props.height;
main.style.left = `${left}px`;
main.style.top = `${top}px`;
main.style.width = `${width}px`;
main.style.height = `${height}px`;
}
onUpdated(resize);
onMounted(() => {
resize();
useDrag(
drag,
dragFn,
(x, y) => {
lastX = x;
lastY = y;
},
() => {
moveSelected.value = false;
},
true
);
if (props.resizable) {
useDrag(
leftB,
leftDrag,
(x, y) => {
right = left.value + width.value;
},
void 0,
true
);
useDrag(
topB,
topDrag,
(x, y) => {
bottom = top.value + height.value;
},
void 0,
true
);
useDrag(rightB, rightDrag, void 0, void 0, true);
useDrag(bottomB, bottomDrag, void 0, void 0, true);
}
});
onUnmounted(() => {
cancelGlobalDrag(dragFn);
if (props.resizable) {
cancelGlobalDrag(leftDrag);
cancelGlobalDrag(topDrag);
cancelGlobalDrag(rightDrag);
cancelGlobalDrag(bottomDrag);
}
});
</script>
<style lang="less" scoped>
.box {
width: 300px;
height: calc(100vh - 100px);
position: fixed;
left: 50px;
top: 50px;
display: flex;
overflow: visible;
font-family: 'normal';
}
.box-main {
width: 100%;
height: 100%;
overflow: hidden;
}
.box-move {
transition: font-size 0.3s ease-out;
position: absolute;
left: -32px;
top: -32px;
width: 32px;
height: 32px;
}
.box-drag {
cursor: all-scroll;
user-select: none;
}
.box-move[selected='false'] {
font-size: 8px;
}
.box-move[selected='true'] {
font-size: 32px;
}
.border {
margin: 0;
position: absolute;
transition: transform 0.3s ease-out;
}
.border-horizontal {
width: 100%;
height: 0px;
left: 0px;
}
.border-horizontal[selected='true'] {
transform: scaleY(300%);
cursor: ns-resize;
}
.border-horizontal:hover,
.border-horizontal:active {
transform: scaleY(500%);
cursor: ns-resize;
}
.border-vertical {
width: 0px;
height: 100%;
top: 0px;
}
.border-vertical[selected='true'] {
transform: scaleX(300%);
cursor: ew-resize;
}
.border-vertical:hover,
.border-vertical:active {
transform: scaleX(500%);
cursor: ew-resize;
}
.border-left {
left: 0;
border-left: 2px solid #ddd9;
}
.border-right {
right: 0;
border-right: 2px solid #ddd9;
}
.border-top {
top: 0;
border-top: 2px solid #ddd9;
}
.border-bottom {
bottom: 0;
border-bottom: 2px solid #ddd9;
}
@media screen and (max-width: 600px) {
.box {
width: calc(100vw - 100px);
height: 250px;
}
}
</style>

View File

@ -13,16 +13,21 @@
import { onMounted, onUnmounted, onUpdated } from 'vue';
import { cancelGlobalDrag, useDrag, useWheel } from '../plugin/use';
let main: HTMLDivElement;
const props = defineProps<{
now?: number;
type?: 'vertical' | 'horizontal';
drag?: boolean;
width?: number;
update?: boolean;
noScroll?: boolean;
}>();
const emits = defineEmits<{
(e: 'update:now', value: number): void;
(e: 'update:drag', value: boolean): void;
(e: 'update:update'): void;
}>();
let now = 0;
@ -54,6 +59,7 @@ function draw() {
} else if (now < 0) {
now = 0;
}
if (props.noScroll) return;
const w = ctx.canvas.width;
const h = ctx.canvas.height;
emits('update:now', now);
@ -86,6 +92,23 @@ async function calHeight() {
const style = getComputedStyle(content);
total = parseFloat(style[canvasAttr]);
res('');
const canvas = ctx.canvas;
const style2 = getComputedStyle(canvas);
canvas.style.width = `${width}px`;
canvas.width = width * scale;
canvas.height = parseFloat(style2.height) * scale;
if (props.noScroll) canvas.style.width = `0px`;
if (props.type === 'horizontal') {
main.style.flexDirection = 'column';
canvas.style.height = `${width}px`;
canvas.style.width = '98%';
canvas.style.margin = '0 1% 0 1%';
canvas.width = parseFloat(style2.width) * scale;
canvas.height = width * scale;
if (props.noScroll) canvas.style.height = `0px`;
}
});
});
}
@ -126,26 +149,13 @@ function contentDrag(x: number, y: number) {
}
onMounted(async () => {
const div = document.getElementById(`scroll-div-${id}`) as HTMLDivElement;
const canvas = document.getElementById(`scroll-${id}`) as HTMLCanvasElement;
main = document.getElementById(`scroll-div-${id}`) as HTMLDivElement;
const d = document.getElementById(`content-${id}`) as HTMLDivElement;
ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
content = d;
const canvas = document.getElementById(`scroll-${id}`) as HTMLCanvasElement;
ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
const style = getComputedStyle(canvas);
canvas.style.width = `${width}px`;
canvas.width = width * scale;
canvas.height = parseFloat(style.height) * scale;
if (props.type === 'horizontal') {
div.style.flexDirection = 'column';
canvas.style.height = `${width}px`;
canvas.style.width = '98%';
canvas.style.margin = '0 1% 0 1%';
canvas.width = parseFloat(style.width) * scale;
canvas.height = width * scale;
}
if (!props.noScroll) {
//
useDrag(
canvas,
@ -161,6 +171,7 @@ onMounted(async () => {
},
true
);
}
//
useDrag(

View File

@ -1,6 +1,8 @@
import { createApp } from 'vue';
import App from './App.vue';
import App2 from './App2.vue';
import './styles.less';
import 'ant-design-vue/dist/antd.dark.css';
createApp(App).mount('#root');
createApp(App2).mount('#root2');

View File

@ -3,10 +3,12 @@ import { Component, markRaw, ref, Ref, watch } from 'vue';
import Book from '../ui/book.vue';
import Toolbox from '../ui/toolbox.vue';
import Equipbox from '../ui/equipbox.vue';
import StatusBar from '../ui/statusBar.vue';
export const bookOpened = ref(false);
export const toolOpened = ref(false);
export const equipOpened = ref(false);
export const showStatusBar = ref(false);
export const transition = ref(true);
export const noClosePanel = ref(false);
@ -40,7 +42,14 @@ export default function init() {
}
});
});
return { uiStack, transition, bookOpened, toolOpened, equipOpened };
return {
uiStack,
transition,
bookOpened,
toolOpened,
equipOpened,
showStatusBar
};
}
async function showApp() {

View File

@ -51,6 +51,7 @@ type ImageIds =
| 'money.png'
| 'mount.jpg'
| 'plot1.jpg'
| 'skill.png'
| 'skill0.png'
| 'skill1.png'
| 'skill2.png'

View File

@ -74,3 +74,9 @@
font-family: Fira Code;
src: url(/src/fonts/FiraCode-Regular.ttf);
}
#non-ui {
position: absolute;
z-index: 999;
user-select: none;
}

View File

@ -47,6 +47,9 @@ interface PluginDeclaration extends PluginUtils {
/** 状态栏信息,取反后刷新状态栏 */
readonly statusBarStatus: Ref<boolean>;
/** 是否显示状态栏 */
readonly showStatusBar: Ref<boolean>;
/** ui栈 */
readonly uiStack: Ref<Component[]>;

View File

@ -242,7 +242,7 @@ onUnmounted(async () => {
#none {
width: 100%;
height: 100%;
font-size: 10vw;
font-size: 6vw;
display: flex;
justify-content: center;
align-items: center;

View File

@ -1,18 +1,117 @@
<template>
<div id="status-bar"></div>
<div id="status-bar">
<Box :resizable="true" v-model:width="width" v-model:height="height">
<Scroll
id="status-main"
v-model:update="updateStatus"
:no-scroll="true"
>
<div id="status-div">
<span id="status-floor">{{ floor }}</span>
<span id="status-lv">{{ lvName }}</span>
<div id="status-skill" class="status-item">
<img
src="/project/images/skill.png"
class="status-icon"
/>
<span>{{ skill }}</span>
</div>
<div id="status-hp" class="status-item">
<img src="/project/images/hp.png" class="status-icon" />
<span>{{ format(hero.hp!) }}</span>
<span id="status-hpmax" class="status-extra"
>+{{ format(hero.hpmax!) }}/t</span
>
<span
v-if="has(spring)"
id="status-spring"
class="status-extra"
>剩余{{ spring }}</span
>
</div>
<div id="status-atk" class="status-item">
<img
src="/project/images/atk.png"
class="status-icon"
/>
<span>{{ format(hero.atk!) }}</span>
<span id="status-mana" class="status-extra"
>+{{ format(hero.mana!) }}</span
>
</div>
<div id="status-def" class="status-item">
<img
src="/project/images/def.png"
class="status-icon"
/>
<span>{{ format(hero.def!) }}</span>
</div>
<div id="status-mdef" class="status-item">
<img src="/project/images/IQ.png" class="status-icon" />
<span>{{ format(hero.mdef!) }}</span>
</div>
<div id="status-money" class="status-item">
<img
src="/project/images/money.png"
class="status-icon"
/>
<span>{{ format(hero.money!) }}</span>
</div>
<div id="status-exp" class="status-item">
<img
src="/project/images/exp.png"
class="status-icon"
/>
<span>{{ format(up) }}</span>
</div>
<div id="status-key" class="status-item">
<span style="color: #fca; padding-left: 10%">{{
keys[0]?.toString().padStart(2, '0')
}}</span>
<span style="color: #aad">{{
keys[1]?.toString().padStart(2, '0')
}}</span>
<span style="color: #f88; padding-right: 10%">{{
keys[2]?.toString().padStart(2, '0')
}}</span>
</div>
<div class="status-item">
<span id="skill-tree" class="button-text">技能树</span>
</div>
<div class="status-item">
<span id="status-skill" class="button-text"
>查看技能</span
>
</div>
</div>
</Scroll>
</Box>
</div>
</template>
<script lang="ts" setup>
import { ref, shallowReactive, watch } from 'vue';
import { isMobile } from '../plugin/use';
import Box from '../components/box.vue';
import Scroll from '../components/scroll.vue';
import { status } from '../plugin/ui/statusBar';
import { isMobile } from '../plugin/use';
import { has } from '../plugin/utils';
const width = ref(isMobile ? window.innerWidth - 100 : 300);
const height = ref(isMobile ? 250 : window.innerHeight - 100);
const updateStatus = ref(false);
const format = core.formatBigNumber;
watch(width, n => (updateStatus.value = !updateStatus.value));
watch(height, n => (updateStatus.value = !updateStatus.value));
const width = ref(isMobile ? window.innerWidth : 300);
const height = ref(isMobile ? 300 : window.innerHeight - 100);
const left = ref(isMobile ? 0 : 50);
const top = ref(50);
const hero = shallowReactive<Partial<HeroStatus>>({});
const keys = shallowReactive<number[]>([]);
const floor = ref<string>();
const lvName = ref<string>();
const skill = ref<string>('无');
const up = ref(0);
const spring = ref<number>();
/**
* 要展示的勇士属性
*/
@ -24,7 +123,8 @@ const toShow: (keyof NumbericHeroStatus)[] = [
'hpmax', //
'mana', //
'money', //
'exp' //
'exp', //
'lv' //
];
watch(status, update);
@ -36,14 +136,135 @@ function update() {
toShow.forEach(v => {
hero[v] = core.getRealStatus(v);
});
keys[0] = core.itemCount('yellowKey');
keys[1] = core.itemCount('blueKey');
keys[2] = core.itemCount('redKey');
floor.value = core.status.thisMap?.title;
lvName.value = core.getLvName(hero.lv);
if (flags.blade && flags.bladeOn) {
skill.value = '断灭之刃';
}
up.value = core.getNextLvUpNeed() ?? 0;
if (core.hasFlag('spring')) {
spring.value = 50 - flags.springCount;
}
}
</script>
<style lang="less" scoped>
#status-bar {
#status-main {
background-color: #0009;
width: 100%;
height: 100%;
position: fixed;
padding: 1vh 0 1vh 0;
}
.status-item {
position: relative;
max-width: 300px;
font-size: 32px;
width: 100%;
margin-bottom: 1vh;
text-shadow: 3px 2px 3px #000, 0px 0px 3px #111;
display: flex;
flex-direction: row;
align-items: center;
}
.status-icon {
width: 48px;
height: 48px;
margin-right: 10%;
margin-left: 10%;
}
#status-header {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
#status-div {
display: flex;
flex-direction: row;
flex-wrap: wrap;
height: 100%;
}
#status-floor {
max-width: 300px;
font-size: 28px;
width: 100%;
text-align: center;
}
#status-lv {
max-width: 300px;
font-size: 28px;
width: 100%;
text-align: center;
}
.status-extra {
position: absolute;
right: 15%;
bottom: 0;
font-size: 24px;
}
#status-mana {
line-height: 1;
text-shadow: 1px 0 0 #c66, 0 1px 0 #c66, -1px 0 0 #c66, 0 -1px 0 #c66;
}
#status-hpmax {
line-height: 1;
text-shadow: 1px 0 0 #0c0, 0 1px 0 #0c0, -1px 0 0 #0c0, 0 -1px 0 #0c0;
}
#status-spring {
line-height: 0;
text-shadow: 1px 0 0 #0c0, 0 1px 0 #0c0, -1px 0 0 #0c0, 0 -1px 0 #0c0;
}
#status-key {
display: flex;
flex-direction: row;
justify-content: space-around;
}
#skill-tree,
#status-skill {
text-align: center;
width: 100%;
}
@media screen and (max-width: 600px) {
.status-item {
max-width: 150px;
font-size: 18px;
}
#status-floor {
max-width: 150px;
font-size: 18px;
width: 100%;
}
#status-lv {
max-width: 150px;
font-size: 18px;
width: 100%;
}
.status-extra {
font-size: 14px;
}
.status-icon {
width: 28px;
height: 28px;
}
}
</style>