运行lint,将theLastPage的内容存储在sessionStorage里

This commit is contained in:
Linus Torvalds
2026-02-27 19:38:16 +08:00
parent bd1d9f2cec
commit 1f8a5f1d98
40 changed files with 663 additions and 514 deletions

3
front/src/app.d.ts vendored
View File

@@ -10,7 +10,4 @@ declare global {
}
}
export {};

View File

@@ -4,9 +4,9 @@
import Settings from 'carbon-icons-svelte/lib/Settings.svelte';
import Home from 'carbon-icons-svelte/lib/Home.svelte';
import Return from 'carbon-icons-svelte/lib/Return.svelte';
import SearchAdvanced from "carbon-icons-svelte/lib/SearchAdvanced.svelte";
import CarbonForIbmDotcom from "carbon-icons-svelte/lib/CarbonForIbmDotcom.svelte";
import EventSchedule from "carbon-icons-svelte/lib/EventSchedule.svelte";
import SearchAdvanced from 'carbon-icons-svelte/lib/SearchAdvanced.svelte';
import CarbonForIbmDotcom from 'carbon-icons-svelte/lib/CarbonForIbmDotcom.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import { page } from '$app/state';
let isAdminView = $derived(page.url.pathname.startsWith('/admin'));
@@ -45,12 +45,15 @@
<span>后台中心</span>
</a>
<a href="/op/ticket_search" class="nav-item" class:active={page.url.pathname === '/op/ticket_search'}>
<a
href="/op/ticket_search"
class="nav-item"
class:active={page.url.pathname === '/op/ticket_search'}
>
<SearchAdvanced />
<span>检索工单</span>
</a>
<a href="/op/scheduler" class="nav-item" class:active={page.url.pathname === '/op/scheduler'}>
<EventSchedule />
<span>我的排班</span>
</a>
@@ -69,12 +72,19 @@
<span>管理中心</span>
</a>
<a href="/admin/add_ticket" class="nav-item" class:active={page.url.pathname === '/admin/add_ticket'}>
<a
href="/admin/add_ticket"
class="nav-item"
class:active={page.url.pathname === '/admin/add_ticket'}
>
<SearchAdvanced />
<span>增添工单</span>
</a>
<a href="/admin/scheduler" class="nav-item" class:active={page.url.pathname === '/admin/scheduler'}>
<a
href="/admin/scheduler"
class="nav-item"
class:active={page.url.pathname === '/admin/scheduler'}
>
<EventSchedule />
<span>成员排班</span>
</a>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { BlockMap } from '$lib/types/enum';
import type { WtsBlock } from '$lib/types/enum';
let { b,r }: { b: WtsBlock, r: string } = $props();
let { b, r }: { b: WtsBlock; r: string } = $props();
</script>
<span>

View File

@@ -4,6 +4,6 @@
let { a }: { a: WtsAccess } = $props();
</script>
<span class="inline-block bg-gray-300 px-3 py-1 text-[15px] rounded-[19px]">
<span class="inline-block rounded-[19px] bg-gray-300 px-3 py-1 text-[15px]">
{AccessMap[a]}
</span>

View File

@@ -14,6 +14,7 @@
canceled: 'text-gray-500'
};
</script>
{#if s === 'scheduled' && isSameDay(ap, new Date())}
<span>
<strong class="text-blue-600">已预约</strong>

View File

@@ -25,7 +25,12 @@
open = $bindable(TicketModal.Opened),
src = TicketModal.SRC,
onTicketChanged
}: { t?: Ticket | null; open?: boolean; src?: 'user' | 'operator'; onTicketChanged?: () => void | Promise<void>;} = $props();
}: {
t?: Ticket | null;
open?: boolean;
src?: 'user' | 'operator';
onTicketChanged?: () => void | Promise<void>;
} = $props();
let loading = $state(false);
let traces: Trace[] = $state([]);
@@ -129,7 +134,15 @@
<NotificationQueue bind:this={q1} />
<ModalHeader title=" 🖋请更新No.{getTid(t)}的状态" />
<ModalBody>
<TraceUpdateView {t} bind:view bind:open bind:isUpReady {q} {q1} onUpdated={onTicketChanged} />
<TraceUpdateView
{t}
bind:view
bind:open
bind:isUpReady
{q}
{q1}
onUpdated={onTicketChanged}
/>
</ModalBody>
<UpViewButton bind:view bind:isUpReady />
</ComposedModal>

View File

@@ -4,10 +4,10 @@
let {
view = $bindable<'trace' | 'cancel' | 'update'>(),
isUpReady = $bindable<boolean>(),
isUpReady = $bindable<boolean>()
}: {
view: 'trace' | 'cancel' | 'update',
isUpReady: boolean,
view: 'trace' | 'cancel' | 'update';
isUpReady: boolean;
} = $props();
</script>

View File

@@ -91,7 +91,7 @@ export function Guard(a: (subject: WtsAccess) => boolean) {
return;
}
if (!a(jwt.access)) {
if(jwt.access === "unregistered"){
if (jwt.access === 'unregistered') {
goto('/register');
return;
}

View File

@@ -1,14 +1,28 @@
import { browser } from '$app/environment';
//全局状态保存用户刚刚访问的页面以便JWT获取后跳转回来
class theLastPage {
p = $state('/');
constructor() {
if (browser) {
this.p = sessionStorage.getItem('_the_last_page') || '/';
}
}
Write(p: string) {
this.p = p;
if (browser) {
sessionStorage.setItem('_the_last_page', p);
}
}
Read(): string {
const p1 = this.p;
this.p = '/';
if (browser) {
sessionStorage.removeItem('_the_last_page');
}
return p1;
}
}

View File

@@ -19,7 +19,7 @@ export let criteria: Criteria = {
category: [],
isp: [],
newer_than: undefined,
older_than: undefined,
older_than: undefined
},
_order: 'priority',
_floor: null,
@@ -38,12 +38,11 @@ export function resetCriteria() {
category: [],
isp: [],
newer_than: undefined,
older_than: undefined,
older_than: undefined
},
_order: 'priority',
_floor: null,
_blocks_in_zone: [],
_view_today_scheduled: false
} as Criteria;
}

View File

@@ -9,7 +9,7 @@ export const sample1issuer: UserProfile = {
phone: '13800138000',
isp: 'mobile',
account: '12345678901@139.gd',
wx: 'zhangsan_wx',
wx: 'zhangsan_wx'
};
export const sample1: Ticket = {
@@ -21,7 +21,7 @@ export const sample1: Ticket = {
category: 'ip-or-device',
status: 'scheduled',
priority: 'assigned',
appointed_at: NowRFC3339(),
appointed_at: NowRFC3339()
};
export const sample2issuer: UserProfile = {
@@ -33,7 +33,7 @@ export const sample2issuer: UserProfile = {
phone: '13800138000',
isp: 'telecom',
account: '18923456789',
wx: 'zhangsan_wx',
wx: 'zhangsan_wx'
};
export const sample2: Ticket = {
@@ -44,7 +44,7 @@ export const sample2: Ticket = {
submitted_at: RFC3339('2024-01-01T00:10:00Z'),
category: 'first-install',
status: 'fresh',
priority: 'mainline',
priority: 'mainline'
};
export const sampleTrace: Trace[] = [
@@ -56,7 +56,7 @@ export const sampleTrace: Trace[] = [
op_name: '(用户操作)',
remark: '工单已提交',
new_status: 'fresh',
new_priority: 'mainline',
new_priority: 'mainline'
},
{
opid: 2,
@@ -66,7 +66,7 @@ export const sampleTrace: Trace[] = [
op_name: '哈哈哈',
remark: '用户预约了时间',
new_status: 'scheduled',
new_appointment: RFC3339('2024-01-13T14:00:00Z'),
new_appointment: RFC3339('2024-01-13T14:00:00Z')
},
{
opid: 3,
@@ -75,7 +75,7 @@ export const sampleTrace: Trace[] = [
op: '2395',
op_name: '啊啊啊',
remark: '材料不足,改日修',
new_status: 'delay',
new_status: 'delay'
},
{
opid: 4,
@@ -85,7 +85,7 @@ export const sampleTrace: Trace[] = [
op_name: '嘿嘿嘿',
remark: '与用户重新约定时间预约在2024-01-21下午',
new_status: 'scheduled',
new_appointment: RFC3339('2024-01-21T15:00:00Z'),
new_appointment: RFC3339('2024-01-21T15:00:00Z')
},
{
opid: 5,
@@ -94,6 +94,6 @@ export const sampleTrace: Trace[] = [
op: '2395',
op_name: '喵喵喵',
remark: '问题解决:用户路由器线路接触不良,更换后恢复正常',
new_status: 'solved',
},
new_status: 'solved'
}
];

View File

@@ -1,32 +1,80 @@
export type WtsBlock =
| '1'| '2'| '3'| '4'| '5'| '6' //凤翔
| '7'| '8'| '9'| '10'| '11' //北门
| '12'| '13'| '14'| '15'| '20'| '21'| '22' //东门
| '16'| '17'| '18'| '19' //岐头
| 'XHA'| 'XHB'| 'XHC'| 'XHD' //香晖
| '1'
| '2'
| '3'
| '4'
| '5'
| '6' //凤翔
| '7'
| '8'
| '9'
| '10'
| '11' //北门
| '12'
| '13'
| '14'
| '15'
| '20'
| '21'
| '22' //东门
| '16'
| '17'
| '18'
| '19' //岐头
| 'XHA'
| 'XHB'
| 'XHC'
| 'XHD' //香晖
| 'ZH' //朝晖
| 'other';
export const BlockMap: Record<WtsBlock, string> = {
'1':'1栋','2':'2栋','3':'3栋','4':'4栋','5':'5栋','6':'6栋',
'7':'7栋','8':'8栋','9':'9栋','10':'10栋','11':'11栋',
'12':'12栋','13':'13栋','14':'14栋','15':'15栋','20':'20栋','21':'21栋','22':'22栋',
'16':'16栋','17':'17栋','18':'18栋','19':'19栋',
'XHA':'香晖A','XHB':'香晖B','XHC':'香晖C','XHD':'香晖D',
'ZH':'朝晖',
'other':'其它',
}
'1': '1栋',
'2': '2栋',
'3': '3栋',
'4': '4栋',
'5': '5栋',
'6': '6栋',
'7': '7栋',
'8': '8栋',
'9': '9栋',
'10': '10栋',
'11': '11栋',
'12': '12栋',
'13': '13栋',
'14': '14栋',
'15': '15栋',
'20': '20栋',
'21': '21栋',
'22': '22栋',
'16': '16栋',
'17': '17栋',
'18': '18栋',
'19': '19栋',
XHA: '香晖A',
XHB: '香晖B',
XHC: '香晖C',
XHD: '香晖D',
ZH: '朝晖',
other: '其它'
};
export type WtsAccess =
| 'dev'
| 'chief'| 'api'| 'group-leader'
| 'formal-member'| 'informal-member'
| 'pre-member'| 'user'
| 'chief'
| 'api'
| 'group-leader'
| 'formal-member'
| 'informal-member'
| 'pre-member'
| 'user'
| 'unregistered';
export const IsAccessIn = (...targets: WtsAccess[]) => (subject: WtsAccess): boolean => {
export const IsAccessIn =
(...targets: WtsAccess[]) =>
(subject: WtsAccess): boolean => {
return targets.includes(subject);
}
};
export const IsOperator = IsAccessIn(
'api',
@@ -37,12 +85,7 @@ export const IsOperator = IsAccessIn(
'informal-member'
);
export const IsAdmin = IsAccessIn(
'group-leader',
'api',
'chief',
'dev'
);
export const IsAdmin = IsAccessIn('group-leader', 'api', 'chief', 'dev');
export const IsUser = IsAccessIn(
'api',
@@ -57,108 +100,129 @@ export const IsUser = IsAccessIn(
export const IsPreMember = IsAccessIn('pre-member');
export const IsFormalMember = IsAccessIn(
'group-leader',
'api',
'chief',
'dev',
'formal-member'
);
export const IsFormalMember = IsAccessIn('group-leader', 'api', 'chief', 'dev', 'formal-member');
export const IsUnregistered = IsAccessIn('unregistered');
export const AccessMap: Record<WtsAccess, string> = {
'dev': '开发组','chief':'科长','api':'API','group-leader':'组长',
'formal-member':'正式成员','informal-member':'实习成员',
'pre-member':'前成员','user':'用户',
'unregistered':'未注册用户',
}
dev: '开发组',
chief: '科长',
api: 'API',
'group-leader': '组长',
'formal-member': '正式成员',
'informal-member': '实习成员',
'pre-member': '前成员',
user: '用户',
unregistered: '未注册用户'
};
export type WtsISP = 'telecom' | 'unicom' | 'mobile' | 'broadnet' | 'others';
export const ISPMap: Record<WtsISP, string> = {
'telecom': '电信',
'unicom': '联通',
'mobile': '移动',
'broadnet': '广电',
'others': '其它',
}
telecom: '电信',
unicom: '联通',
mobile: '移动',
broadnet: '广电',
others: '其它'
};
export type WtsStatus = 'fresh' | 'scheduled' | 'delay' | 'escalated' | 'solved' | 'canceled';
export const StatusMap: Record<WtsStatus, string> = {
'fresh': '待解决',
'scheduled': '已预约',
'delay': '改日修',
'escalated': '已上报',
'solved': '已解决',
'canceled': '已取消',
}
fresh: '待解决',
scheduled: '已预约',
delay: '改日修',
escalated: '已上报',
solved: '已解决',
canceled: '已取消'
};
export type WtsPriority = 'highest' | 'assigned' | 'mainline' | 'normal' | 'in-passing' | 'least';
export const PriorityMap: Record<WtsPriority, string> = {
'highest': '>>紧急派单!<<',
'assigned': '运营商工单',
'mainline': '主线任务',
'normal': '普通报修',
highest: '>>紧急派单!<<',
assigned: '运营商工单',
mainline: '主线任务',
normal: '普通报修',
'in-passing': '顺路看看',
'least': '最低',
}
least: '最低'
};
export type WtsCategory =
| 'first-install'| 'low-speed'| 'ip-or-device'| 'client-or-account'| 'others';
| 'first-install'
| 'low-speed'
| 'ip-or-device'
| 'client-or-account'
| 'others';
export const CategoryMap: Record<WtsCategory, string> = {
'first-install': '新装',
'low-speed': '网速慢',
'ip-or-device': 'IP或设备问题',
'client-or-account': '客户端或账号问题',
'others': '其它问题',
}
others: '其它问题'
};
export type WtsAPIErrorType =
| 1 | 2 | 3 | 4 | 5;
export type WtsAPIErrorType = 1 | 2 | 3 | 4 | 5;
export const APIErrorTypeMap: Record<WtsAPIErrorType, string> = {
1: '服务器内部错误,请联系我们的技术人员。',
2: '你的请求无效,可能是由于格式错误或不支持的操作。',
3: '您没有进行该操作的权限。',
4: '数据库出现错误。',
5: '您的请求在逻辑上不被允许。',
}
5: '您的请求在逻辑上不被允许。'
};
export type WtsZone = 'FX' | 'BM' | 'DM' | 'QT' | 'XHAB' | 'XHCD' | 'ZH' | 'other' | 'all';
export const ZoneMap: Record<WtsZone, string> = {
'FX':'凤翔',
'BM':'北门',
'DM':'东门',
'QT':'岐头',
'XHAB':'香晖AB',
'XHCD':'香晖CD',
'ZH':'朝晖',
'other':'其它',
'all':'全部',
}
FX: '凤翔',
BM: '北门',
DM: '东门',
QT: '岐头',
XHAB: '香晖AB',
XHCD: '香晖CD',
ZH: '朝晖',
other: '其它',
all: '全部'
};
export const ZoneToBlock: Record<WtsZone, WtsBlock[]> = {
'FX':['1','2','3','4','5','6'],
'BM':['7','8','9','10','11'],
'DM':['12','13','14','15','20','21','22'],
'QT':['16','17','18','19'],
'XHAB':['XHA','XHB'],
'XHCD':['XHC','XHD'],
'ZH':['ZH'],
'other':['other'],
'all':[
'1','2','3','4','5','6',
'7','8','9','10','11',
'12','13','14','15','20','21','22',
'16','17','18','19',
'XHA','XHB',
'XHC','XHD',
FX: ['1', '2', '3', '4', '5', '6'],
BM: ['7', '8', '9', '10', '11'],
DM: ['12', '13', '14', '15', '20', '21', '22'],
QT: ['16', '17', '18', '19'],
XHAB: ['XHA', 'XHB'],
XHCD: ['XHC', 'XHD'],
ZH: ['ZH'],
other: ['other'],
all: [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'20',
'21',
'22',
'16',
'17',
'18',
'19',
'XHA',
'XHB',
'XHC',
'XHD',
'ZH',
'other'
],
}
]
};

View File

@@ -41,12 +41,15 @@
<div style="display: flex; flex-direction: column; gap: 0.25rem;">
<h2>🥺不会使用校园网?</h2>
<p>点击下方按钮,我们准备了校园网方方面面的攻略!</p>
<Button href="/help" kind="secondary" style="align-self: flex-end;transform: translate(-25px,0px);">网络攻略</Button>
<Button
href="/help"
kind="secondary"
style="align-self: flex-end;transform: translate(-25px,0px);">网络攻略</Button
>
</div>
</div>
</RetroCard>
<br />
<br />
<br />

View File

@@ -2,7 +2,10 @@
<br />
<hr />
<br />
<p>中山学院网络维护科是一个独立的<strong>学生组织</strong>成立于2005年接受校信息中心的指导和管理。</p>
<p>
中山学院网络维护科是一个独立的<strong>学生组织</strong
>成立于2005年接受校信息中心的指导和管理。
</p>
<p>
网维的主要任务是保证校园宿舍网络的正常运行,处理解决在校学生所报修的有线校园网问题,这项业务主要通过依托于微信的网络报修系统来运行。
</p>
@@ -11,7 +14,8 @@
目前我们与三大运营商建立了良好的合作关系承接运营商在校园内的装维业务在信息中心的关怀下我们也承担学校部分IT后勤工作。
</p>
<p>
对于学生校园网报修,我们的成员会在<strong>每天16:30~18:00</strong>统一上门解决问题,用户在这个时间段必须本人在宿舍。
对于学生校园网报修,我们的成员会在<strong>每天16:30~18:00</strong
>统一上门解决问题,用户在这个时间段必须本人在宿舍。
</p>
<p>
网维一般会在每年的9~10月招新对象为当年新生要求有基础的计算机知识。具体招新事宜请以实际通告为准。

View File

@@ -1,7 +1,7 @@
<script>
import RetroCard from "$lib/components/RetroCard.svelte";
import RetroCard from '$lib/components/RetroCard.svelte';
</script>
<h1>隐私权条款</h1>
<br />
<hr />
@@ -61,4 +61,3 @@
<hr />
<br />
<p>本条款最后更新于2025年12月27日最终解释权归网维所有。</p>

View File

@@ -30,7 +30,7 @@
let q: NotificationQueue;
let r = $state({
priority: 'normal',
priority: 'normal'
} as NewTicketReq);
function onOccurDateChange(event: CustomEvent) {
@@ -223,4 +223,3 @@ helperText="选择工单的优先级类型,更高优先级会在系统中优
<NotificationQueue bind:this={q} />
<Loading active={!notLoading} />

View File

@@ -3,5 +3,6 @@
let { data }: PageProps = $props();
</script>
<h1>Forbidden</h1>
<p>对不起,您没有权限访问这个页面。</p>

View File

@@ -3,11 +3,11 @@
@plugin '@tailwindcss/typography';
@plugin 'daisyui';
@plugin "daisyui/theme" {
name: "wireframe";
@plugin 'daisyui/theme' {
name: 'wireframe';
default: true;
prefersdark: false;
color-scheme: "light";
color-scheme: 'light';
--color-base-100: #f4f4f4;
--color-base-200: #e6e6e6;
--color-base-300: #d5d5d5;
@@ -38,9 +38,7 @@
--noise: 1;
}
@layer base {
html {
/* 移动端优化:防止点击高亮 */
-webkit-tap-highlight-color: transparent;
@@ -55,5 +53,4 @@
/* 移动端优化:防止水平溢出 */
@apply min-h-screen overflow-x-hidden bg-base-100 text-base-content;
}
}

View File

@@ -11,7 +11,7 @@
function gotoAuthAPI() {
if (!env.PUBLIC_JWT) {
console.log("未找到PUBLIC_JWT")
console.log('未找到PUBLIC_JWT');
}
if (dev && env.PUBLIC_JWT) {
docCookies.setItem('jwt', env.PUBLIC_JWT, Infinity, '/');

View File

@@ -67,7 +67,12 @@
<RetroCard>
<span style="display: flex; align-items: center;">
<h2 style="margin-right: 0.5rem;">个人信息</h2>
<Renew onclick={() => {TheLastPage.Write('/me'),goto('/login')}} style="cursor: pointer;" />
<Renew
onclick={() => {
(TheLastPage.Write('/me'), goto('/login'));
}}
style="cursor: pointer;"
/>
</span>
<StructuredList style="margin-bottom: 1rem;">
<StructuredListBody>

View File

@@ -115,7 +115,10 @@
<div class="zone-tiles">
{#each zoneDisplayOrder as zone}
{#if typeof countByZone?.[zone] !== 'undefined'}
<Tile class={`zone-tile zone-${zoneTone(countByZone?.[zone])}`} on:click={() => jumpSearch(zone)}>
<Tile
class={`zone-tile zone-${zoneTone(countByZone?.[zone])}`}
on:click={() => jumpSearch(zone)}
>
<span class="zone-name">{ZoneMap[zone]}</span>
<span class="zone-count">{countByZone?.[zone] ?? 0}</span>
</Tile>

View File

@@ -29,13 +29,15 @@
import { criteria } from '$lib/states/ticketCriteriaSearch.svelte';
import { goto } from '$app/navigation';
import type { WtsStatus, WtsPriority, WtsCategory, WtsISP } from '$lib/types/enum';
import {WtsJWT} from '$lib/jwt'
import { WtsJWT } from '$lib/jwt';
onMount(() => Guard(IsOperator));
let token: WtsJWT = $state({} as WtsJWT);
onMount(()=>{token =CheckAndGetJWT('parsed');})
onMount(() => {
token = CheckAndGetJWT('parsed');
});
let req = $state(criteria.r as FilterTicketsReq);
@@ -116,7 +118,7 @@
'delay',
'escalated'
] as const satisfies readonly WtsStatus[];
const statusOptions: readonly WtsStatus[] = statusOptionsAdmin //之前的区分没有意义,在后端会拦截的,在这里高花样,好像反而会破坏正常功能,感觉这个页面还是重写的样子。。
const statusOptions: readonly WtsStatus[] = statusOptionsAdmin; //之前的区分没有意义,在后端会拦截的,在这里高花样,好像反而会破坏正常功能,感觉这个页面还是重写的样子。。
const priorityOptions = [
'highest',
@@ -184,7 +186,7 @@
$effect(() => {
isScheduledSelected = req.status?.includes('scheduled') ?? false;
})
});
</script>
<h1>报修单检索</h1>
@@ -244,7 +246,10 @@
</Grid>
</CheckboxGroup>
<div class="toggle">
<Toggle size="sm" toggled={allSelected(zoneSelected, zoneOptions)} on:toggle={(e)=> {
<Toggle
size="sm"
toggled={allSelected(zoneSelected, zoneOptions)}
on:toggle={(e) => {
const { toggled } = e.detail as { toggled: boolean };
zoneSelected = toggled ? [...zoneOptions] : [];
}}
@@ -283,7 +288,10 @@
</Grid>
</CheckboxGroup>
<div class="toggle">
<Toggle size="sm" toggled={allSelected(req.status, statusOptions)} on:toggle={(e)=> {
<Toggle
size="sm"
toggled={allSelected(req.status, statusOptions)}
on:toggle={(e) => {
const { toggled } = e.detail as { toggled: boolean };
req.status = toggled ? [...statusOptions] : [];
}}
@@ -320,7 +328,10 @@
</Grid>
</CheckboxGroup>
<div class="toggle">
<Toggle size="sm" toggled={allSelected(req.priority, priorityOptions)} on:toggle={(e)=> {
<Toggle
size="sm"
toggled={allSelected(req.priority, priorityOptions)}
on:toggle={(e) => {
const { toggled } = e.detail as { toggled: boolean };
req.priority = toggled ? [...priorityOptions] : [];
}}
@@ -353,7 +364,10 @@
</Grid>
</CheckboxGroup>
<div class="toggle">
<Toggle size="sm" toggled={allSelected(req.category, categoryOptions)} on:toggle={(e)=> {
<Toggle
size="sm"
toggled={allSelected(req.category, categoryOptions)}
on:toggle={(e) => {
const { toggled } = e.detail as { toggled: boolean };
req.category = toggled ? [...categoryOptions] : [];
}}
@@ -388,7 +402,10 @@
</Grid>
</CheckboxGroup>
<div class="toggle">
<Toggle size="sm" toggled={allSelected(req.isp, ispOptions)} on:toggle={(e)=> {
<Toggle
size="sm"
toggled={allSelected(req.isp, ispOptions)}
on:toggle={(e) => {
const { toggled } = e.detail as { toggled: boolean };
req.isp = toggled ? [...ispOptions] : [];
}}
@@ -410,12 +427,23 @@
<br />
<br />
<NumberInput min={1} max={20} step={1} bind:value={floor} allowEmpty={true} allowDecimal={false}
labelText="只看如下楼层(不填代表查看全部楼层)" />
<NumberInput
min={1}
max={20}
step={1}
bind:value={floor}
allowEmpty={true}
allowDecimal={false}
labelText="只看如下楼层(不填代表查看全部楼层)"
/>
<br />
<br />
<Toggle labelText="只显示预约在今天的预约单" bind:toggled={viewTodayScheduled} disabled={!isScheduledSelected} />
<Toggle
labelText="只显示预约在今天的预约单"
bind:toggled={viewTodayScheduled}
disabled={!isScheduledSelected}
/>
<br />
<br />
<Button on:click={search}>搜索</Button>

View File

@@ -40,8 +40,6 @@
const roomNum = Number.parseInt(digits, 10);
if (!Number.isFinite(roomNum)) return null;
const floor = Math.floor(roomNum / 100);
//console.log('getFloorFromRoom', { room, digits, roomNum, floor });
return Number.isFinite(floor) ? floor : null;
@@ -63,7 +61,11 @@
if (criteria._view_today_scheduled) {
const todayStart = new Date().setHours(0, 0, 0, 0);
const todayEnd = new Date().setHours(23, 59, 59, 999);
tickets = tickets.filter((t) => t.status !== 'scheduled' || (toMs(t.appointed_at) >= todayStart && toMs(t.appointed_at) <= todayEnd));
tickets = tickets.filter(
(t) =>
t.status !== 'scheduled' ||
(toMs(t.appointed_at) >= todayStart && toMs(t.appointed_at) <= todayEnd)
);
}
ok = true;
@@ -123,6 +125,11 @@
{:else}
<span>没有找到符合条件的报修单。</span>
{/if}
<TicketDetail t={TicketModal.NowTicket} bind:open={TicketModal.Opened} src={TicketModal.SRC} onTicketChanged={refreshTickets}/>
<TicketDetail
t={TicketModal.NowTicket}
bind:open={TicketModal.Opened}
src={TicketModal.SRC}
onTicketChanged={refreshTickets}
/>
<NotificationQueue bind:this={q} />

View File

@@ -69,6 +69,11 @@
<UserTicket {t} />
{/each}
<TicketDetail t={TicketModal.NowTicket} bind:open={TicketModal.Opened} src={TicketModal.SRC} onTicketChanged={refreshTickets1}/>
<TicketDetail
t={TicketModal.NowTicket}
bind:open={TicketModal.Opened}
src={TicketModal.SRC}
onTicketChanged={refreshTickets1}
/>
<NotificationQueue bind:this={q} />