@ -0,0 +1,21 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"courseselected"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,588 @@
|
|||||||
|
<Modal visible={change} style={{background: 'white'}} width="800px" title="编辑讨论内容"
|
||||||
|
onCancel={e=>{setChange(false)}}
|
||||||
|
onOk={e=>{setChange(false)}}
|
||||||
|
footer={<div>
|
||||||
|
<Button onClick={e=>{
|
||||||
|
const temp=[...taolonglist]
|
||||||
|
setyulan(taolonglist)
|
||||||
|
setChange(false)}}
|
||||||
|
>保存</Button>
|
||||||
|
<Button onClick={e=>setChange(false)}>关闭</Button>
|
||||||
|
</div>}
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<Card
|
||||||
|
><table ><tbody>
|
||||||
|
|
||||||
|
{ taolonglist.map((value,index)=>(
|
||||||
|
<React.Fragment >
|
||||||
|
<tr><td style={{width:"550px"}}>
|
||||||
|
<React.Fragment >
|
||||||
|
{value.lx==="wb"?<h4 style={{width:"550px"}}> {value.wb}</h4>:null}
|
||||||
|
{value.lx==="pic"? <img
|
||||||
|
src={!value.show?value.pic:value.xiugaipic}
|
||||||
|
width="550px" height="auto"/> : null}
|
||||||
|
{value.lx==="video"?<video src={!value.show? value.sp:value.xiugaisp} width="550px" height="auto" controls={true}></video>:null}
|
||||||
|
{value.lx==="download"?<a href={value.url} style={{width:"550px"}} >{value.title}</a>:null}
|
||||||
|
|
||||||
|
</React.Fragment></td>
|
||||||
|
|
||||||
|
<td ><Button onClick={e=>{
|
||||||
|
let temp=[...taolonglist]
|
||||||
|
temp[index].show=!temp[index].show;
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}>修改</Button><Popconfirm
|
||||||
|
title="确认要删除此项吗"
|
||||||
|
onConfirm={event => {
|
||||||
|
let temp=[...taolonglist]
|
||||||
|
temp.splice(index,1)
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}
|
||||||
|
onCancel={event => alert("no")}
|
||||||
|
okText="确认"
|
||||||
|
cancelText="不"
|
||||||
|
>
|
||||||
|
<Button onClick={e=>{
|
||||||
|
|
||||||
|
}} href="#">删除</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{value.show ?
|
||||||
|
<tr>
|
||||||
|
<td colSpan={3}>
|
||||||
|
<React.Fragment>
|
||||||
|
<h3>编辑</h3>
|
||||||
|
<Tabs activeKey={value.leixing} onChange={e => {
|
||||||
|
let temp = [...taolonglist]
|
||||||
|
temp[index].leixing = e;
|
||||||
|
settaolonglist(temp);
|
||||||
|
}} tabBarStyle={{}} style={{width:"720px"}}>
|
||||||
|
<Tabs.TabPane tab="视频" key="video">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<video src={value.xiugaisp} width="600px"
|
||||||
|
height="auto"
|
||||||
|
style={{display: "none"}}/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
视频路径
|
||||||
|
<Input value={value.xiugailujing}
|
||||||
|
onChange={event => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].xiugailujing = event.target.value;
|
||||||
|
settaolonglist(temp);
|
||||||
|
|
||||||
|
}} width="550px"
|
||||||
|
style={{width:"550px"}}
|
||||||
|
></Input>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td><Button onClick={e => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].xiugaisp = temp[index].xiugailujing;
|
||||||
|
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}>打开</Button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Upload
|
||||||
|
name='file'
|
||||||
|
action={HOUDUAN+'api/uploadtm'}
|
||||||
|
headers={{
|
||||||
|
th: value.uuid,
|
||||||
|
xx: fujianuuid,
|
||||||
|
lx: "kaoshi"
|
||||||
|
}}
|
||||||
|
onChange={
|
||||||
|
(info) => {
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
console.log(info.file, info.fileList);
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
//setTms(uuid.v4())
|
||||||
|
message.success(`${info.file.name} file uploaded successfully`);
|
||||||
|
|
||||||
|
_.delay(() => {
|
||||||
|
console.log('Done after 1 second');
|
||||||
|
const temp = [...taolonglist];
|
||||||
|
temp[index].xiugaisp = ZHIYUE+ value.uuid + "/" + fujianuuid + path.extname(info.file.name);
|
||||||
|
settaolonglist(temp);
|
||||||
|
setFujianuuid(uuid.v4())
|
||||||
|
//settaolonglist(uuid.v4())
|
||||||
|
}, 200);
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showUploadList={false}><Button>
|
||||||
|
<Icon type="upload"/>上传附件
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Button onClick={e => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].sp = temp[index].xiugaisp;
|
||||||
|
temp[index].lx = temp[index].leixing
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}>确认</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab="图片" key="pic">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src={value.xiugaipic} width="600px"
|
||||||
|
height="auto"
|
||||||
|
style={{display: "none"}}/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span>图片路径
|
||||||
|
|
||||||
|
<Input value={value.xiugailujing} onChange={event => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].xiugailujing = event.target.value;
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}
|
||||||
|
width="550px"
|
||||||
|
style={{width:"550px"}}
|
||||||
|
></Input></span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td><Button onClick={e => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].xiugaipic = temp[index].xiugailujing;
|
||||||
|
settaolonglist(temp);
|
||||||
|
//settaolonglist(uuid.v4())
|
||||||
|
}}>打开</Button></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Upload
|
||||||
|
name='file'
|
||||||
|
action={HOUDUAN+'api/uploadtm'}
|
||||||
|
headers={{
|
||||||
|
th: value.uuid,
|
||||||
|
xx: fujianuuid,
|
||||||
|
lx: "kaoshi"
|
||||||
|
}}
|
||||||
|
onChange={
|
||||||
|
(info) => {
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
console.log(info.file, info.fileList);
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
//setTms(uuid.v4())
|
||||||
|
message.success(`${info.file.name} file uploaded successfully`);
|
||||||
|
|
||||||
|
_.delay(() => {
|
||||||
|
console.log('Done after 1 second');
|
||||||
|
const temp = [...taolonglist];
|
||||||
|
temp[index].xiugaipic = ZHIYUE+value.uuid + "/" + fujianuuid + path.extname(info.file.name);
|
||||||
|
settaolonglist(temp);
|
||||||
|
setFujianuuid(uuid.v4());
|
||||||
|
|
||||||
|
}, 200);
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showUploadList={false}><Button>
|
||||||
|
<Icon type="upload"/>上传附件
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><Button onClick={e => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].pic = temp[index].xiugaipic;
|
||||||
|
temp[index].lx = temp[index].leixing
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}>保存</Button></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab="附件" key="download">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>文件标题</td>
|
||||||
|
<td style={{width: "400px"}}>
|
||||||
|
<Input value={value.xiugai} onChange={e => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].xiugai = e.target.value;
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}
|
||||||
|
width="550px"
|
||||||
|
style={{width:"550px"}}
|
||||||
|
></Input>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
文件路径
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Input value={value.xiugaiwj}
|
||||||
|
onChange={event => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].xiugaiwj = event.target.value;
|
||||||
|
temp[index].lx = temp[index].leixing
|
||||||
|
settaolonglist(temp);
|
||||||
|
|
||||||
|
}}
|
||||||
|
width="550px"
|
||||||
|
style={{width:"550px"}}
|
||||||
|
></Input></td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Upload
|
||||||
|
name='file'
|
||||||
|
action={HOUDUAN+'api/uploadtm'}
|
||||||
|
headers={{
|
||||||
|
th: value.uuid,
|
||||||
|
xx: fujianuuid,
|
||||||
|
lx: "kaoshi"
|
||||||
|
}}
|
||||||
|
onChange={
|
||||||
|
(info) => {
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
console.log(info.file, info.fileList);
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
//setTms(uuid.v4())
|
||||||
|
message.success(`${info.file.name} file uploaded successfully`);
|
||||||
|
|
||||||
|
_.delay(() => {
|
||||||
|
console.log('Done after 1 second');
|
||||||
|
const temp = [...taolonglist];
|
||||||
|
temp[index].xiugaiwj = ZHIYUE+value.uuid + "/" + fujianuuid + path.extname(info.file.name);
|
||||||
|
temp[index].xiugai = info.file.name;
|
||||||
|
settaolonglist(temp);
|
||||||
|
setFujianuuid(uuid.v4())
|
||||||
|
}, 200);
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showUploadList={false}><Button>
|
||||||
|
<Icon type="upload"/>上传附件
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
<Button onClick={e => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].url = temp[index].xiugaiwj;
|
||||||
|
temp[index].title = temp[index].xiugai;
|
||||||
|
temp[index].lx = temp[index].leixing
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}>保存</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab="文字" key="wb">
|
||||||
|
|
||||||
|
<Input.TextArea value={value.xiugaiwb}
|
||||||
|
onChange={event => {
|
||||||
|
let temp = [...taolonglist];
|
||||||
|
temp[index].xiugaiwb = event.target.value;
|
||||||
|
temp[index].lx = temp[index].leixing
|
||||||
|
settaolonglist(temp);
|
||||||
|
}} width="550px"
|
||||||
|
style={{width:"550px"}}
|
||||||
|
></Input.TextArea>
|
||||||
|
<Button onClick={e => {
|
||||||
|
let temp = [...taolonglist]
|
||||||
|
temp[index].wb = temp[index].xiugaiwb;
|
||||||
|
settaolonglist(temp);
|
||||||
|
}}>修改</Button>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
</Tabs>
|
||||||
|
</React.Fragment>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
:null}
|
||||||
|
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
|
||||||
|
</tbody></table></Card>
|
||||||
|
<Card>
|
||||||
|
<Tabs activeKey={add.leixing} onChange={e=>{
|
||||||
|
let temp={...add};
|
||||||
|
|
||||||
|
temp.leixing=e;
|
||||||
|
|
||||||
|
setAdd(temp)
|
||||||
|
}}>
|
||||||
|
<Tabs.TabPane tab="视频" key="video">
|
||||||
|
<table><tbody>
|
||||||
|
<tr ><td >
|
||||||
|
<video src={add.xiugaisp} width="600px" height="auto" controls={true}/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr><td>
|
||||||
|
<Upload
|
||||||
|
name='file'
|
||||||
|
action={HOUDUAN+'api/uploadtm'}
|
||||||
|
headers={{
|
||||||
|
th: add.uuid ,
|
||||||
|
xx: fujianuuid,
|
||||||
|
lx: "kaoshi"
|
||||||
|
}}
|
||||||
|
onChange={
|
||||||
|
(info) => {
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
console.log(info.file, info.fileList);
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
//setTms(uuid.v4())
|
||||||
|
message.success(`${info.file.name} file uploaded successfully`);
|
||||||
|
|
||||||
|
_.delay(() => {
|
||||||
|
console.log('Done after 1 second');
|
||||||
|
const temp={...add};
|
||||||
|
temp.xiugaisp=ZHIYUE+add.uuid+"/"+fujianuuid + path.extname(info.file.name);
|
||||||
|
setAdd(temp);
|
||||||
|
setFujianuuid(uuid.v4())
|
||||||
|
}, 200);
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showUploadList={false}><Button>
|
||||||
|
<Icon type="upload"/>上传附件
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
</td></tr>
|
||||||
|
<tr>
|
||||||
|
<td>文件路径
|
||||||
|
<Input value={add.url} width="400px" onChange={event => {
|
||||||
|
let temp={...add};
|
||||||
|
temp.url=event.target.value;
|
||||||
|
setAdd(temp);
|
||||||
|
}}></Input>
|
||||||
|
<Button onClick={e=>{
|
||||||
|
let temp={...add};
|
||||||
|
temp.xiugaisp=temp.url;
|
||||||
|
setAdd(temp);
|
||||||
|
}
|
||||||
|
}>应用</Button>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab="图片" key="pic">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src={add.xiugaipic} width="600px" height="auto"/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Upload
|
||||||
|
name='file'
|
||||||
|
action={HOUDUAN+'api/uploadtm'}
|
||||||
|
headers={{
|
||||||
|
th: add.uuid,
|
||||||
|
xx: fujianuuid,
|
||||||
|
lx: "kaoshi"
|
||||||
|
}}
|
||||||
|
onChange={
|
||||||
|
(info) => {
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
console.log(info.file, info.fileList);
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
//setTms(uuid.v4())
|
||||||
|
message.success(`${info.file.name} file uploaded successfully`);
|
||||||
|
|
||||||
|
_.delay(() => {
|
||||||
|
console.log('Done after 1 second');
|
||||||
|
const temp = {...add};
|
||||||
|
temp.xiugaipic =ZHIYUE+ add.uuid + "/" + fujianuuid + path.extname(info.file.name);
|
||||||
|
setAdd(temp);
|
||||||
|
setFujianuuid(uuid.v4())
|
||||||
|
}, 200);
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showUploadList={false}><Button>
|
||||||
|
<Icon type="upload"/>上传附件
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>文件路径
|
||||||
|
<Input value={add.url} width="400px" onChange={event => {
|
||||||
|
let temp = {...add};
|
||||||
|
temp.url = event.target.value;
|
||||||
|
setAdd(temp);
|
||||||
|
}}></Input>
|
||||||
|
<Button onClick={e => {
|
||||||
|
let temp = {...add};
|
||||||
|
temp.xiugaipic = temp.url;
|
||||||
|
setAdd(temp);
|
||||||
|
}
|
||||||
|
}>应用</Button>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab="附件" key="download">
|
||||||
|
<tr>
|
||||||
|
<td><a href={add.xiugailujing}>{add.xiugai}</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>文件标题</td>
|
||||||
|
<td >
|
||||||
|
<Input value={add.xiugai} onChange={event => {
|
||||||
|
let temp={...add}
|
||||||
|
temp.xiugai=event.target.value;
|
||||||
|
setAdd(temp);
|
||||||
|
}}></Input>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>文件路径</td>
|
||||||
|
<td style={{width:"350px"}}>
|
||||||
|
<Input value={add.xiugailujing} onChange={event => {
|
||||||
|
let temp={...add};
|
||||||
|
temp.xiugailujing=event.target.value;
|
||||||
|
setAdd(temp);
|
||||||
|
}}></Input></td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Upload
|
||||||
|
name='file'
|
||||||
|
action={HOUDUAN+'api/uploadtm'}
|
||||||
|
headers={{
|
||||||
|
th: add.uuid ,
|
||||||
|
xx: fujianuuid,
|
||||||
|
lx: "kaoshi"
|
||||||
|
}}
|
||||||
|
onChange={
|
||||||
|
(info) => {
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
console.log(info.file, info.fileList);
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
//setTms(uuid.v4())
|
||||||
|
message.success(`${info.file.name} file uploaded successfully`);
|
||||||
|
|
||||||
|
_.delay(() => {
|
||||||
|
|
||||||
|
console.log('Done after 1 second');
|
||||||
|
const temp = {...add};
|
||||||
|
temp.xiugai=info.file.name;
|
||||||
|
temp.xiugailujing = ZHIYUE+ add.uuid + "/" + fujianuuid + path.extname(info.file.name)
|
||||||
|
setAdd(temp)
|
||||||
|
setFujianuuid(uuid.v4());
|
||||||
|
|
||||||
|
}, 200);
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showUploadList={false}><Button>
|
||||||
|
<Icon type="upload"/>上传附件
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
</td></tr>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab="文字" key="wb">
|
||||||
|
<Input.TextArea value={add.xiugaiwb} onChange={event => {
|
||||||
|
let temp={...add};
|
||||||
|
temp.xiugaiwb=event.target.value;
|
||||||
|
setAdd(temp);
|
||||||
|
}} style={{height:"250px"}}></Input.TextArea>
|
||||||
|
</Tabs.TabPane>
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
<Button onClick={
|
||||||
|
// {value.lx==="wb"?<h4>{value.wb}</h4>:null}
|
||||||
|
// {value.lx==="pic"? <img
|
||||||
|
// src={value.pic}
|
||||||
|
// width="600px" height="auto"/> : null}
|
||||||
|
// {value.lx==="video"?<video src={value.sp} width="600px" height="auto" controls={true}></video>:null}
|
||||||
|
// {value.lx==="download"?<a href={value.url}>{value.title}</a>:null}
|
||||||
|
e=>{
|
||||||
|
const xiang={
|
||||||
|
lx:add.leixing,
|
||||||
|
leixing:add.leixing,
|
||||||
|
wb:add.xiugaiwb,
|
||||||
|
xiugaiwb:add.xiugaiwb,
|
||||||
|
pic:add.xiugaipic,
|
||||||
|
xiugaiwj:add.xiugailujing,//修改附件路径
|
||||||
|
xiugaipic:add.xiugaipic,
|
||||||
|
sp:add.xiugaisp,
|
||||||
|
xiugaisp:add.xiugaisp,
|
||||||
|
|
||||||
|
xiugailujing:add.xiugailujing,
|
||||||
|
url:add.xiugailujing,
|
||||||
|
xiugai:add.xiugai,
|
||||||
|
title:add.xiugai,
|
||||||
|
show:false
|
||||||
|
|
||||||
|
}
|
||||||
|
let temp=[...taolonglist]
|
||||||
|
temp.push(xiang);
|
||||||
|
settaolonglist(temp);
|
||||||
|
const addtemp={leixing:add.leixing,uuid:uuid.v4()}
|
||||||
|
setAdd(addtemp);
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
添加
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
</Modal>
|
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script>
|
||||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||||
|
CSS.supports('top: constant(a)'))
|
||||||
|
document.write(
|
||||||
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||||
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||||
|
</script>
|
||||||
|
<title></title>
|
||||||
|
<!--preload-links-->
|
||||||
|
<!--app-context-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"><!--app-html--></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"name": "uni-preset-vue",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev:custom": "uni -p",
|
||||||
|
"dev:h5": "uni",
|
||||||
|
"dev:h5:ssr": "uni --ssr",
|
||||||
|
"dev:mp-alipay": "uni -p mp-alipay",
|
||||||
|
"dev:mp-baidu": "uni -p mp-baidu",
|
||||||
|
"dev:mp-jd": "uni -p mp-jd",
|
||||||
|
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||||
|
"dev:mp-lark": "uni -p mp-lark",
|
||||||
|
"dev:mp-qq": "uni -p mp-qq",
|
||||||
|
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||||
|
"dev:mp-weixin": "uni -p mp-weixin",
|
||||||
|
"dev:mp-xhs": "uni -p mp-xhs",
|
||||||
|
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||||
|
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||||
|
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||||
|
"build:custom": "uni build -p",
|
||||||
|
"build:h5": "uni build",
|
||||||
|
"build:h5:ssr": "uni build --ssr",
|
||||||
|
"build:mp-alipay": "uni build -p mp-alipay",
|
||||||
|
"build:mp-baidu": "uni build -p mp-baidu",
|
||||||
|
"build:mp-jd": "uni build -p mp-jd",
|
||||||
|
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||||
|
"build:mp-lark": "uni build -p mp-lark",
|
||||||
|
"build:mp-qq": "uni build -p mp-qq",
|
||||||
|
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||||
|
"build:mp-weixin": "uni build -p mp-weixin",
|
||||||
|
"build:mp-xhs": "uni build -p mp-xhs",
|
||||||
|
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||||
|
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||||
|
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
|
||||||
|
"type-check": "vue-tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-app": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-app-harmony": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-app-plus": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-components": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-h5": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-alipay": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-baidu": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-jd": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-kuaishou": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-lark": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-qq": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-toutiao": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-weixin": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-ui": "^1.5.7",
|
||||||
|
"@hyoga/uni-socket.io": "^3.0.4",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"vue": "^3.4.21",
|
||||||
|
"vue-i18n": "^9.1.9"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@dcloudio/types": "^3.4.8",
|
||||||
|
"@dcloudio/uni-automator": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-cli-shared": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/uni-stacktracey": "3.0.0-4030620241128001",
|
||||||
|
"@dcloudio/vite-plugin-uni": "3.0.0-4030620241128001",
|
||||||
|
"@vue/runtime-core": "^3.4.21",
|
||||||
|
"@vue/tsconfig": "^0.1.3",
|
||||||
|
"sass": "^1.69.0",
|
||||||
|
"sass-loader": "^10.1.1",
|
||||||
|
"typescript": "^4.9.4",
|
||||||
|
"vite": "5.2.8",
|
||||||
|
"vue-tsc": "^1.0.24"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"pages": [
|
||||||
|
// ... existing code ...
|
||||||
|
|
||||||
|
// ... existing code ...
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
/// <reference types='@dcloudio/types' />
|
||||||
|
import 'vue'
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
type Hooks = App.AppInstance & Page.PageInstance;
|
||||||
|
|
||||||
|
interface ComponentCustomOptions extends Hooks {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
|
||||||
|
onLaunch(() => {
|
||||||
|
console.log("App Launch");
|
||||||
|
});
|
||||||
|
onShow(() => {
|
||||||
|
console.log("App Show");
|
||||||
|
});
|
||||||
|
onHide(() => {
|
||||||
|
console.log("App Hide");
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style></style>
|
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<view
|
||||||
|
class="tech-button"
|
||||||
|
:class="[type, size]"
|
||||||
|
@click="$emit('click')"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'primary', // primary | secondary
|
||||||
|
validator: (value) => ['primary', 'secondary'].includes(value)
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: 'normal', // normal | small
|
||||||
|
validator: (value) => ['normal', 'small'].includes(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tech-button {
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
border-radius: 50rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(2rpx);
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(74, 144, 226, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
color: #4facfe;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.5);
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: rgba(31, 47, 71, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.normal {
|
||||||
|
height: 80rpx;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
height: 60rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active::before {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<view
|
||||||
|
class="tech-card"
|
||||||
|
:class="{ active }"
|
||||||
|
@click="$emit('click')"
|
||||||
|
>
|
||||||
|
<view class="card-bg"></view>
|
||||||
|
<view class="card-content">
|
||||||
|
<uni-icons
|
||||||
|
:type="icon"
|
||||||
|
size="28"
|
||||||
|
:color="active ? '#4facfe' : 'rgba(255,255,255,0.7)'"
|
||||||
|
/>
|
||||||
|
<text class="card-title">{{ title }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-decoration"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
title: String,
|
||||||
|
icon: String,
|
||||||
|
active: Boolean
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tech-card {
|
||||||
|
height: 140rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
.card-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border: 1px solid rgba(74, 144, 226, 0.2);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-top: 15rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-decoration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
background: radial-gradient(circle, rgba(74, 144, 226, 0.3) 0%, transparent 70%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
transform: translateY(-5rpx);
|
||||||
|
box-shadow: 0 10rpx 20rpx rgba(0, 242, 254, 0.2);
|
||||||
|
|
||||||
|
.card-bg {
|
||||||
|
background: rgba(31, 47, 71, 0.8);
|
||||||
|
border-color: rgba(74, 144, 226, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-decoration {
|
||||||
|
background: radial-gradient(circle, rgba(74, 144, 226, 0.6) 0%, transparent 70%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tech-progress">
|
||||||
|
<view
|
||||||
|
class="progress-bar"
|
||||||
|
:style="{ width: `${percent}%` }"
|
||||||
|
>
|
||||||
|
<view class="progress-highlight"></view>
|
||||||
|
</view>
|
||||||
|
<text class="progress-text">{{ percent }}%</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
percent: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
validator: (value) => value >= 0 && value <= 100
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tech-progress {
|
||||||
|
height: 16rpx;
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background: linear-gradient(90deg, #00f2fe 0%, #4facfe 100%);
|
||||||
|
position: relative;
|
||||||
|
transition: width 0.6s ease;
|
||||||
|
|
||||||
|
.progress-highlight {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(90deg, rgba(255,255,255,0.3) 0%, transparent 100%);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
position: absolute;
|
||||||
|
right: 10rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,8 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.vue' {
|
||||||
|
import { DefineComponent } from 'vue'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import { createSSRApp } from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
export function createApp() {
|
||||||
|
const app = createSSRApp(App);
|
||||||
|
return {
|
||||||
|
app,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"name" : "",
|
||||||
|
"appid" : "",
|
||||||
|
"description" : "",
|
||||||
|
"versionName" : "1.0.0",
|
||||||
|
"versionCode" : "100",
|
||||||
|
"transformPx" : false,
|
||||||
|
/* 5+App特有相关 */
|
||||||
|
"app-plus" : {
|
||||||
|
"usingComponents" : true,
|
||||||
|
"nvueStyleCompiler" : "uni-app",
|
||||||
|
"compilerVersion" : 3,
|
||||||
|
"splashscreen" : {
|
||||||
|
"alwaysShowBeforeRender" : true,
|
||||||
|
"waiting" : true,
|
||||||
|
"autoclose" : true,
|
||||||
|
"delay" : 0
|
||||||
|
},
|
||||||
|
/* 模块配置 */
|
||||||
|
"modules" : {},
|
||||||
|
/* 应用发布信息 */
|
||||||
|
"distribute" : {
|
||||||
|
/* android打包配置 */
|
||||||
|
"android" : {
|
||||||
|
"permissions" : [
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/* ios打包配置 */
|
||||||
|
"ios" : {},
|
||||||
|
/* SDK配置 */
|
||||||
|
"sdkConfigs" : {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 快应用特有相关 */
|
||||||
|
"quickapp" : {},
|
||||||
|
/* 小程序特有相关 */
|
||||||
|
"mp-weixin" : {
|
||||||
|
"appid" : "",
|
||||||
|
"setting" : {
|
||||||
|
"urlCheck" : false
|
||||||
|
},
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-alipay" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-baidu" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"mp-toutiao" : {
|
||||||
|
"usingComponents" : true
|
||||||
|
},
|
||||||
|
"uniStatistics": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"vueVersion" : "3"
|
||||||
|
}
|
@ -0,0 +1,158 @@
|
|||||||
|
{
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/index/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "首页",
|
||||||
|
"enablePullDownRefresh": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/learning/learning",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "课程学习"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/management/management",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "课程管理"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/schedule/schedule",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "智能课程表",
|
||||||
|
"navigationBarBackgroundColor": "#0a192f",
|
||||||
|
"navigationBarTextStyle": "white",
|
||||||
|
"backgroundColor": "#0a192f"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/user/user",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "个人中心"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/leave/leave",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "leave"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/plan/plan",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "plan"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/course/course",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "course"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/score/score",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "score"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/settings/settings",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "settings"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/management/leave",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "leave"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/management/program",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "program"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/management/transcript",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "transcript"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/management/selection",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "selection"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/list/list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "list"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/discussion/discussion",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "discussion"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/checkin/checkin",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "checkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/quiz/quiz",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "quiz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/exam/exam",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "exam"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/shangke/shangke",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "shnagke"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tabBar": {
|
||||||
|
"color": "#7A7E83",
|
||||||
|
"selectedColor": "#007AFF",
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"pagePath": "pages/learning/learning",
|
||||||
|
"text": "课程学习",
|
||||||
|
"iconPath": "static/edu-tabs/learning.png",
|
||||||
|
"selectedIconPath": "static/edu-tabs/learning-active.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/schedule/schedule",
|
||||||
|
"text": "课程表",
|
||||||
|
"iconPath": "/static/edu-tabs/calendar.png",
|
||||||
|
"selectedIconPath": "/static/edu-tabs/calendar-active.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/management/management",
|
||||||
|
"text": "课程管理",
|
||||||
|
"iconPath": "/static/edu-tabs/dashboard.png",
|
||||||
|
"selectedIconPath": "/static/edu-tabs/dashboard-active.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/user/user",
|
||||||
|
"text": "我的",
|
||||||
|
"iconPath": "/static/edu-tabs/user.png",
|
||||||
|
"selectedIconPath": "/static/edu-tabs/user-active.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view>签到</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tech-container">
|
||||||
|
<!-- 科技风顶部导航 -->
|
||||||
|
<view class="tech-header">
|
||||||
|
<view class="header-content">
|
||||||
|
<text class="header-title">我的课程中心</text>
|
||||||
|
<view class="header-line"></view>
|
||||||
|
</view>
|
||||||
|
<view class="tech-pattern"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 课程内容区域 -->
|
||||||
|
<view class="course-content">
|
||||||
|
<!-- 课程分类导航 -->
|
||||||
|
<scroll-view class="category-nav" scroll-x>
|
||||||
|
<view
|
||||||
|
v-for="(category, index) in categories"
|
||||||
|
:key="index"
|
||||||
|
class="category-item"
|
||||||
|
:class="{ active: activeCategory === index }"
|
||||||
|
@click="switchCategory(index)"
|
||||||
|
>
|
||||||
|
<text>{{ category }}</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 课程列表 -->
|
||||||
|
<view class="course-list">
|
||||||
|
<view
|
||||||
|
v-for="(course, index) in filteredCourses"
|
||||||
|
:key="index"
|
||||||
|
class="course-card"
|
||||||
|
>
|
||||||
|
<image class="course-cover" :src="course.cover" mode="aspectFill" />
|
||||||
|
<view class="course-info">
|
||||||
|
<view class="title-wrapper">
|
||||||
|
<text class="course-title">{{ course.title }}</text>
|
||||||
|
<text class="course-teacher">{{ course.teacher }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="progress-bar">
|
||||||
|
<view
|
||||||
|
class="progress-fill"
|
||||||
|
:style="{ width: `${course.progress}%` }"
|
||||||
|
></view>
|
||||||
|
</view>
|
||||||
|
<text class="progress-text">已学 {{ course.progress }}%</text>
|
||||||
|
</view>
|
||||||
|
<view class="tech-corner"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 科技风底部装饰 -->
|
||||||
|
<view class="tech-footer">
|
||||||
|
<view class="footer-line"></view>
|
||||||
|
<view class="footer-dots">
|
||||||
|
<view class="dot"></view>
|
||||||
|
<view class="dot"></view>
|
||||||
|
<view class="dot"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
|
// 课程分类数据
|
||||||
|
const categories = ref(['全部', '进行中', '已完成', '未开始', '收藏'])
|
||||||
|
const activeCategory = ref(0)
|
||||||
|
|
||||||
|
// 课程数据
|
||||||
|
const courses = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Vue3高级开发实战',
|
||||||
|
teacher: '张老师',
|
||||||
|
cover: '/static/student-course/bg.png',
|
||||||
|
progress: 65,
|
||||||
|
category: '进行中'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: 'React全栈开发',
|
||||||
|
teacher: '李老师',
|
||||||
|
cover: '/static/student-course/bg.png',
|
||||||
|
progress: 30,
|
||||||
|
category: '进行中'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: 'Node.js后端开发',
|
||||||
|
teacher: '王老师',
|
||||||
|
cover: '/static/course3.jpg',
|
||||||
|
progress: 100,
|
||||||
|
category: '已完成'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// 切换分类
|
||||||
|
const switchCategory = (index) => {
|
||||||
|
activeCategory.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤课程
|
||||||
|
const filteredCourses = computed(() => {
|
||||||
|
if (activeCategory.value === 0) return courses.value
|
||||||
|
const categoryMap = ['全部', '进行中', '已完成', '未开始', '收藏']
|
||||||
|
return courses.value.filter(course =>
|
||||||
|
course.category === categoryMap[activeCategory.value]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tech-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #0f1621;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-header {
|
||||||
|
position: relative;
|
||||||
|
padding: 40rpx 30rpx 30rpx;
|
||||||
|
background: linear-gradient(135deg, #1a2a3a 0%, #0f1621 100%);
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 42rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
background: linear-gradient(to right, #4facfe, #00f2fe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-line {
|
||||||
|
height: 4rpx;
|
||||||
|
width: 80rpx;
|
||||||
|
background: linear-gradient(to right, #4facfe, #00f2fe);
|
||||||
|
margin-top: 15rpx;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-pattern {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 300rpx;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, rgba(79, 172, 254, 0.1) 0%, transparent 70%);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-nav {
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.category-item {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 15rpx 30rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
border-radius: 40rpx;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: linear-gradient(to right, #4facfe, #00f2fe);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 5rpx 15rpx rgba(79, 172, 254, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card {
|
||||||
|
position: relative;
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.2);
|
||||||
|
|
||||||
|
.course-cover {
|
||||||
|
width: 100%;
|
||||||
|
height: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-info {
|
||||||
|
padding: 25rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-teacher {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #4facfe;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 8rpx;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to right, #4facfe, #00f2fe);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(79, 172, 254, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-corner {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 60rpx 60rpx 0;
|
||||||
|
border-color: transparent rgba(79, 172, 254, 0.2) transparent transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-footer {
|
||||||
|
padding: 30rpx 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.footer-line {
|
||||||
|
height: 1rpx;
|
||||||
|
background: linear-gradient(to right, transparent, rgba(79, 172, 254, 0.3), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-dots {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: 10rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(79, 172, 254, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view>讨论</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view>课程考试</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
@ -0,0 +1,253 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tech-welcome-container">
|
||||||
|
<!-- 科技风背景元素 -->
|
||||||
|
<view class="tech-background">
|
||||||
|
<view class="tech-circle circle-1"></view>
|
||||||
|
<view class="tech-circle circle-2"></view>
|
||||||
|
<view class="tech-line line-1"></view>
|
||||||
|
<view class="tech-line line-2"></view>
|
||||||
|
<view class="tech-dots">
|
||||||
|
<view class="dot" v-for="i in 20" :key="i" :style="getDotStyle(i)"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 主要内容区 -->
|
||||||
|
<view class="welcome-content">
|
||||||
|
<!-- 动态科技感标题 -->
|
||||||
|
<view class="tech-title">
|
||||||
|
<text class="title-text">只为遇见</text>
|
||||||
|
<text class="title-text highlight">更好的你</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载进度条 -->
|
||||||
|
<view class="tech-progress">
|
||||||
|
<view class="progress-bar">
|
||||||
|
<view class="progress-fill" :style="{width: progress + '%'}"></view>
|
||||||
|
</view>
|
||||||
|
<text class="progress-text">{{progress}}%</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部科技风装饰 -->
|
||||||
|
<view class="tech-footer">
|
||||||
|
<view class="footer-line"></view>
|
||||||
|
<text class="footer-text">智慧教育平台</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
|
||||||
|
const progress = ref(0)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 模拟加载进度
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
progress.value += Math.floor(Math.random() * 10) + 5
|
||||||
|
if (progress.value >= 100) {
|
||||||
|
progress.value = 100
|
||||||
|
clearInterval(timer)
|
||||||
|
// 加载完成后跳转到身份选择页面
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/learning/learning'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生成随机点样式
|
||||||
|
const getDotStyle = (index) => {
|
||||||
|
const size = Math.random() * 6 + 2
|
||||||
|
return {
|
||||||
|
width: `${size}px`,
|
||||||
|
height: `${size}px`,
|
||||||
|
left: `${Math.random() * 100}%`,
|
||||||
|
top: `${Math.random() * 100}%`,
|
||||||
|
opacity: Math.random() * 0.5 + 0.3,
|
||||||
|
animationDelay: `${index * 0.1}s`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tech-welcome-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, #0a0e21 0%, #121a3a 100%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 科技风背景元素 */
|
||||||
|
.tech-background {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.tech-circle {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(0, 240, 255, 0.05), transparent 70%);
|
||||||
|
|
||||||
|
&.circle-1 {
|
||||||
|
width: 600rpx;
|
||||||
|
height: 600rpx;
|
||||||
|
top: -300rpx;
|
||||||
|
right: -300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.circle-2 {
|
||||||
|
width: 500rpx;
|
||||||
|
height: 500rpx;
|
||||||
|
bottom: -250rpx;
|
||||||
|
left: -250rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-line {
|
||||||
|
position: absolute;
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.2), transparent);
|
||||||
|
|
||||||
|
&.line-1 {
|
||||||
|
top: 30%;
|
||||||
|
left: -100px;
|
||||||
|
right: -100px;
|
||||||
|
transform: rotate(-5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.line-2 {
|
||||||
|
top: 60%;
|
||||||
|
left: -100px;
|
||||||
|
right: -100px;
|
||||||
|
transform: rotate(5deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-dots {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #00f0ff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: float 3s infinite ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-content {
|
||||||
|
position: relative;
|
||||||
|
width: 80%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 科技感标题 */
|
||||||
|
.tech-title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 80rpx;
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
display: block;
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
|
||||||
|
&.highlight {
|
||||||
|
background: linear-gradient(to right, #00f0ff, #0088ff);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 56rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 科技风进度条 */
|
||||||
|
.tech-progress {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 60rpx;
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 8rpx;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.3), transparent);
|
||||||
|
animation: shine 2s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #00f0ff, #0088ff);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部装饰 */
|
||||||
|
.tech-footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 60rpx;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.footer-line {
|
||||||
|
width: 100px;
|
||||||
|
height: 2px;
|
||||||
|
background: linear-gradient(90deg, transparent, #00f0ff, transparent);
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画效果 */
|
||||||
|
@keyframes float {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-10px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shine {
|
||||||
|
0% { transform: translateX(-100%); }
|
||||||
|
100% { transform: translateX(100%); }
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,285 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tech-container">
|
||||||
|
<!-- 科技风背景装饰 -->
|
||||||
|
<view class="tech-bg">
|
||||||
|
<view class="tech-line line-1"></view>
|
||||||
|
<view class="tech-line line-2"></view>
|
||||||
|
<view class="tech-dot dot-1"></view>
|
||||||
|
<view class="tech-dot dot-2"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 功能导航入口 -->
|
||||||
|
<view class="nav-grid">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in navItems"
|
||||||
|
:key="index"
|
||||||
|
class="nav-item"
|
||||||
|
@click="navigateTo(item.page)"
|
||||||
|
>
|
||||||
|
<view class="nav-icon" :style="{ background: item.bgColor }">
|
||||||
|
<!-- 使用uni-icons或图片作为图标 -->
|
||||||
|
<image v-if="item.icon.includes('.png')" :src="item.icon" mode="aspectFit" style="width: 32rpx; height: 32rpx;"/>
|
||||||
|
<uni-icons v-else :type="item.icon" size="40" color="#fff"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<text class="nav-text">{{ item.name }}</text>
|
||||||
|
<text class="nav-action">立即进入</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 我的课程 -->
|
||||||
|
<view class="my-course">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">我的课程 (1)</text>
|
||||||
|
<text class="section-more">查看全部 </text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="course-card">
|
||||||
|
<image class="course-cover" src="/static/student-course/bg.png" mode="aspectFill"></image>
|
||||||
|
<view class="course-info">
|
||||||
|
<text class="course-name">军事理论</text>
|
||||||
|
<text class="course-subtitle">军事理论-综合版</text>
|
||||||
|
<view class="progress-bar">
|
||||||
|
<view class="progress-active" style="width: 100%"></view>
|
||||||
|
</view>
|
||||||
|
<text class="progress-text">已完成 100%</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
// 导航项数据
|
||||||
|
const navItems = ref([
|
||||||
|
{
|
||||||
|
name: '讨论',
|
||||||
|
icon: '/static/student-course/discussion.png', // 确保图片路径正确
|
||||||
|
page: '/pages/discussion/discussion'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '签到',
|
||||||
|
icon: '/static/student-course/checkin.png',
|
||||||
|
page: '/pages/checkin/checkin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '答题',
|
||||||
|
icon: '/static/student-course/quiz.png',
|
||||||
|
page: '/pages/quiz/quiz'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '考试',
|
||||||
|
icon: '/static/student-course/exam.png',
|
||||||
|
page: '/pages/exam/exam'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '扫码',
|
||||||
|
icon: '/static/student-course/scan.png',
|
||||||
|
page: 'scan'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// 导航到子页面
|
||||||
|
const navigateTo = (url) => {
|
||||||
|
if(url === 'scan') {
|
||||||
|
uni.scanCode({
|
||||||
|
success: function (res) {
|
||||||
|
console.log('条码类型:' + res.scanType);
|
||||||
|
console.log('条码内容:' + res.result);
|
||||||
|
console.log('/pages/shangke/shangke?uuid='+JSON.parse(res.result).uuid+'&kch='+JSON.parse(res.result).kch+'&kctime='+JSON.parse(res.result).kctime)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/shangke/shangke?uuid='+JSON.parse(res.result).uuid+'&kch='+JSON.parse(res.result).kch+'&kctime='+JSON.parse(res.result).kctime
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tech-container {
|
||||||
|
position: relative;
|
||||||
|
padding: 30rpx;
|
||||||
|
background-color: #0a0e21;
|
||||||
|
color: #fff;
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 科技风背景 */
|
||||||
|
.tech-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: -1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.tech-line {
|
||||||
|
position: absolute;
|
||||||
|
height: 1rpx;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.2), transparent);
|
||||||
|
|
||||||
|
&.line-1 {
|
||||||
|
top: 30%;
|
||||||
|
left: -100rpx;
|
||||||
|
right: -100rpx;
|
||||||
|
transform: rotate(-5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.line-2 {
|
||||||
|
top: 60%;
|
||||||
|
left: -100rpx;
|
||||||
|
right: -100rpx;
|
||||||
|
transform: rotate(5deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-dot {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: rgba(0, 240, 255, 0.05);
|
||||||
|
|
||||||
|
&.dot-1 {
|
||||||
|
width: 400rpx;
|
||||||
|
height: 400rpx;
|
||||||
|
top: -200rpx;
|
||||||
|
right: -200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dot-2 {
|
||||||
|
width: 300rpx;
|
||||||
|
height: 300rpx;
|
||||||
|
bottom: -150rpx;
|
||||||
|
left: -150rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 导航网格 */
|
||||||
|
.nav-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 40rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
position: relative;
|
||||||
|
background-color: rgba(20, 30, 50, 0.6);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
border: 1rpx solid rgba(0, 240, 255, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon {
|
||||||
|
width: 75rpx;
|
||||||
|
height: 75rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-action {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #00f0ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 我的课程 */
|
||||||
|
.my-course {
|
||||||
|
background-color: rgba(20, 30, 50, 0.6);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
border: 1rpx solid rgba(0, 240, 255, 0.1);
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-more {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #00f0ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-card {
|
||||||
|
display: flex;
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.course-cover {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 150rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-info {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20rpx;
|
||||||
|
|
||||||
|
.course-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #aaa;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 8rpx;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.progress-active {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to right, #00f0ff, #0088ff);
|
||||||
|
border-radius: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #00f0ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<text class="title">请假</text>
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
// 这里可以添加页面逻辑
|
||||||
|
const pageTitle = ref('请假')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view>课程列表</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
@ -0,0 +1,134 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tech-container">
|
||||||
|
<!-- 科技风格头部 -->
|
||||||
|
<view class="tech-header">
|
||||||
|
<view class="header-content">
|
||||||
|
<text class="header-title">课程管理中心</text>
|
||||||
|
<view class="header-subtitle">Course Management System</view>
|
||||||
|
</view>
|
||||||
|
<view class="header-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view class="content-wrapper">
|
||||||
|
<!-- 导航卡片 -->
|
||||||
|
<view class="nav-grid">
|
||||||
|
<TechCard
|
||||||
|
v-for="(item, index) in navItems"
|
||||||
|
:key="index"
|
||||||
|
:title="item.title"
|
||||||
|
:icon="item.icon"
|
||||||
|
:active="activeIndex === index"
|
||||||
|
@click="switchTab(index)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 修改后的子页面容器 -->
|
||||||
|
<view class="page-container">
|
||||||
|
<LeavePage v-if="activeIndex === 0" />
|
||||||
|
<ProgramPage v-if="activeIndex === 1" />
|
||||||
|
<TranscriptPage v-if="activeIndex === 2" />
|
||||||
|
<SelectionPage v-if="activeIndex === 3" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import TechCard from '@/components/managemet/TechCard.vue'
|
||||||
|
import LeavePage from './leave.vue'
|
||||||
|
import ProgramPage from './program.vue'
|
||||||
|
import TranscriptPage from './transcript.vue'
|
||||||
|
import SelectionPage from './selection.vue'
|
||||||
|
|
||||||
|
// 导航项数据
|
||||||
|
const navItems = ref([
|
||||||
|
{ title: '请假申请', icon: 'calendar-remove' },
|
||||||
|
{ title: '培养方案', icon: 'book' },
|
||||||
|
{ title: '成绩单', icon: 'document-text' },
|
||||||
|
{ title: '选课中心', icon: 'checkmark-circle' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const activeIndex = ref(0)
|
||||||
|
|
||||||
|
// 切换标签页
|
||||||
|
const switchTab = (index) => {
|
||||||
|
activeIndex.value = index
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tech-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #0a0e17;
|
||||||
|
color: #fff;
|
||||||
|
font-family: 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-header {
|
||||||
|
position: relative;
|
||||||
|
padding: 40rpx 30rpx 60rpx;
|
||||||
|
background: linear-gradient(135deg, #1a2a3a 0%, #0a0e17 100%);
|
||||||
|
z-index: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 42rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 1rpx;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
margin-top: 10rpx;
|
||||||
|
letter-spacing: 1rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-decoration {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
padding: 0 20rpx 40rpx;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
margin-top: -30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
min-height: 600rpx;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(74, 144, 226, 0.2);
|
||||||
|
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,289 @@
|
|||||||
|
<template>
|
||||||
|
<view class="program-page">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<view class="info-section">
|
||||||
|
<view class="section-title">
|
||||||
|
<text class="title-text">培养方案</text>
|
||||||
|
<view class="title-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-grid">
|
||||||
|
<view class="info-item">
|
||||||
|
<uni-icons type="medal" size="20" color="#4facfe"></uni-icons>
|
||||||
|
<text class="info-label">专业名称</text>
|
||||||
|
<text class="info-value">计算机科学与技术</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item">
|
||||||
|
<uni-icons type="flag" size="20" color="#4facfe"></uni-icons>
|
||||||
|
<text class="info-label">培养层次</text>
|
||||||
|
<text class="info-value">本科</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item">
|
||||||
|
<uni-icons type="calendar" size="20" color="#4facfe"></uni-icons>
|
||||||
|
<text class="info-label">学制</text>
|
||||||
|
<text class="info-value">4年</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="info-item">
|
||||||
|
<uni-icons type="star" size="20" color="#4facfe"></uni-icons>
|
||||||
|
<text class="info-label">毕业学分</text>
|
||||||
|
<text class="info-value">160学分</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 课程体系 -->
|
||||||
|
<view class="course-section">
|
||||||
|
<view class="section-title">
|
||||||
|
<text class="title-text">课程体系</text>
|
||||||
|
<view class="title-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<uni-collapse>
|
||||||
|
<uni-collapse-item
|
||||||
|
v-for="(item, index) in courseSystem"
|
||||||
|
:key="index"
|
||||||
|
:title="item.name"
|
||||||
|
:show-animation="true"
|
||||||
|
:border="false"
|
||||||
|
>
|
||||||
|
<view class="course-list">
|
||||||
|
<view
|
||||||
|
v-for="(course, cIndex) in item.courses"
|
||||||
|
:key="cIndex"
|
||||||
|
class="course-item"
|
||||||
|
>
|
||||||
|
<text class="course-name">{{ course.name }}</text>
|
||||||
|
<view class="course-meta">
|
||||||
|
<text class="course-credit">{{ course.credit }}学分</text>
|
||||||
|
<text class="course-type">{{ course.type }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-collapse-item>
|
||||||
|
</uni-collapse>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 学分进度 -->
|
||||||
|
<view class="progress-section">
|
||||||
|
<view class="section-title">
|
||||||
|
<text class="title-text">学分进度</text>
|
||||||
|
<view class="title-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="progress-list">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in creditProgress"
|
||||||
|
:key="index"
|
||||||
|
class="progress-item"
|
||||||
|
>
|
||||||
|
<view class="progress-header">
|
||||||
|
<text class="category">{{ item.category }}</text>
|
||||||
|
<text class="value">{{ item.completed }}/{{ item.total }}学分</text>
|
||||||
|
</view>
|
||||||
|
<TechProgress :percent="item.percent" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import TechProgress from '@/components/managemet/TechProgress.vue'
|
||||||
|
|
||||||
|
const courseSystem = ref([
|
||||||
|
{
|
||||||
|
name: '通识教育课程',
|
||||||
|
courses: [
|
||||||
|
{ name: '大学英语', credit: 8, type: '必修' },
|
||||||
|
{ name: '高等数学', credit: 10, type: '必修' },
|
||||||
|
{ name: '大学物理', credit: 6, type: '必修' },
|
||||||
|
{ name: '思想政治理论', credit: 12, type: '必修' },
|
||||||
|
{ name: '人文艺术选修', credit: 4, type: '选修' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '专业基础课程',
|
||||||
|
courses: [
|
||||||
|
{ name: '程序设计基础', credit: 4, type: '必修' },
|
||||||
|
{ name: '数据结构', credit: 4, type: '必修' },
|
||||||
|
{ name: '计算机组成原理', credit: 4, type: '必修' },
|
||||||
|
{ name: '操作系统', credit: 4, type: '必修' },
|
||||||
|
{ name: '离散数学', credit: 3, type: '必修' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '专业核心课程',
|
||||||
|
courses: [
|
||||||
|
{ name: '算法设计与分析', credit: 4, type: '必修' },
|
||||||
|
{ name: '计算机网络', credit: 4, type: '必修' },
|
||||||
|
{ name: '数据库系统', credit: 4, type: '必修' },
|
||||||
|
{ name: '软件工程', credit: 4, type: '必修' },
|
||||||
|
{ name: '人工智能基础', credit: 3, type: '选修' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '实践环节',
|
||||||
|
courses: [
|
||||||
|
{ name: '课程设计', credit: 6, type: '必修' },
|
||||||
|
{ name: '毕业实习', credit: 4, type: '必修' },
|
||||||
|
{ name: '毕业论文', credit: 8, type: '必修' },
|
||||||
|
{ name: '创新创业实践', credit: 2, type: '选修' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const creditProgress = ref([
|
||||||
|
{ category: '通识教育', completed: 32, total: 40, percent: 80 },
|
||||||
|
{ category: '专业基础', completed: 15, total: 20, percent: 75 },
|
||||||
|
{ category: '专业核心', completed: 8, total: 16, percent: 50 },
|
||||||
|
{ category: '实践环节', completed: 4, total: 20, percent: 20 }
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.program-page {
|
||||||
|
.section-title {
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #4facfe;
|
||||||
|
font-weight: 600;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20rpx;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-decoration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -10rpx;
|
||||||
|
left: 0;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 25rpx;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
margin: 10rpx 0 5rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-section {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
|
||||||
|
:deep(.uni-collapse) {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.uni-collapse-item__title) {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
padding: 20rpx 25rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.uni-collapse-item__wrap) {
|
||||||
|
background: rgba(16, 24, 40, 0.6);
|
||||||
|
border: none;
|
||||||
|
border-radius: 0 0 12rpx 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-list {
|
||||||
|
padding: 0 25rpx 20rpx;
|
||||||
|
|
||||||
|
.course-item {
|
||||||
|
padding: 20rpx 0;
|
||||||
|
border-bottom: 1rpx solid rgba(74, 144, 226, 0.1);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.course-credit {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #4facfe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-type {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-list {
|
||||||
|
.progress-item {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 25rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
.progress-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
|
||||||
|
.category {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #4facfe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,614 @@
|
|||||||
|
<template>
|
||||||
|
<view class="selection-page">
|
||||||
|
<!-- 头部信息 -->
|
||||||
|
<view class="header-section">
|
||||||
|
<view class="header-content">
|
||||||
|
<text class="title">选课中心</text>
|
||||||
|
<text class="subtitle">Course Selection System</text>
|
||||||
|
</view>
|
||||||
|
<view class="header-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 选课状态卡片 -->
|
||||||
|
<view class="status-cards">
|
||||||
|
<view class="status-card">
|
||||||
|
<view class="card-content">
|
||||||
|
<text class="card-label">可选学分</text>
|
||||||
|
<text class="card-value">{{ availableCredits }}</text>
|
||||||
|
<text class="card-unit">学分</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="status-card">
|
||||||
|
<view class="card-content">
|
||||||
|
<text class="card-label">已选学分</text>
|
||||||
|
<text class="card-value">{{ selectedCredits }}</text>
|
||||||
|
<text class="card-unit">学分</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-decoration"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 选课进度 -->
|
||||||
|
<view class="progress-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">选课进度</text>
|
||||||
|
<view class="section-decoration"></view>
|
||||||
|
</view>
|
||||||
|
<TechProgress :percent="selectionProgress" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 课程筛选 -->
|
||||||
|
<view class="filter-section">
|
||||||
|
<view class="filter-tabs">
|
||||||
|
<view
|
||||||
|
v-for="(tab, index) in tabs"
|
||||||
|
:key="index"
|
||||||
|
class="filter-tab"
|
||||||
|
:class="{ active: activeTab === index }"
|
||||||
|
@click="changeTab(index)"
|
||||||
|
>
|
||||||
|
<text>{{ tab }}</text>
|
||||||
|
<view class="tab-indicator"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="filter-options">
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="semesters"
|
||||||
|
@change="handleSemesterChange"
|
||||||
|
>
|
||||||
|
<view class="filter-option">
|
||||||
|
<text>{{ currentSemester || '选择学期' }}</text>
|
||||||
|
<uni-icons type="arrowdown" size="16" color="#4facfe"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="categories"
|
||||||
|
range-key="name"
|
||||||
|
@change="handleCategoryChange"
|
||||||
|
>
|
||||||
|
<view class="filter-option">
|
||||||
|
<text>{{ currentCategory.name || '课程类别' }}</text>
|
||||||
|
<uni-icons type="arrowdown" size="16" color="#4facfe"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 课程列表 -->
|
||||||
|
<view class="course-list">
|
||||||
|
<view
|
||||||
|
v-for="(course, index) in filteredCourses"
|
||||||
|
:key="index"
|
||||||
|
class="course-card"
|
||||||
|
>
|
||||||
|
<view class="course-header">
|
||||||
|
<text class="course-name">{{ course.name }}</text>
|
||||||
|
<text class="course-credit">{{ course.credit }}学分</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="course-meta">
|
||||||
|
<view class="meta-item">
|
||||||
|
<uni-icons type="person" size="16" color="rgba(255,255,255,0.6)"></uni-icons>
|
||||||
|
<text>{{ course.teacher }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="meta-item">
|
||||||
|
<uni-icons type="calendar" size="16" color="rgba(255,255,255,0.6)"></uni-icons>
|
||||||
|
<text>{{ course.time }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="meta-item">
|
||||||
|
<uni-icons type="location" size="16" color="rgba(255,255,255,0.6)"></uni-icons>
|
||||||
|
<text>{{ course.location }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="course-footer">
|
||||||
|
<text class="remaining">剩余名额: {{ course.remaining }}/{{ course.capacity }}</text>
|
||||||
|
|
||||||
|
<TechButton
|
||||||
|
size="small"
|
||||||
|
:type="course.selected ? 'secondary' : 'primary'"
|
||||||
|
@click="toggleSelection(course)"
|
||||||
|
>
|
||||||
|
{{ course.selected ? '已选' : '选课' }}
|
||||||
|
</TechButton>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作栏 -->
|
||||||
|
<view class="action-bar">
|
||||||
|
<view class="selected-info">
|
||||||
|
<text>已选 {{ selectedCount }} 门课程</text>
|
||||||
|
<text>{{ selectedCredits }}学分</text>
|
||||||
|
</view>
|
||||||
|
<TechButton @click="handleSubmit">提交选课</TechButton>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import TechProgress from '@/components/managemet/TechProgress.vue'
|
||||||
|
import TechButton from '@/components/managemet/TechButton.vue'
|
||||||
|
|
||||||
|
// 选课状态数据
|
||||||
|
const availableCredits = ref(25)
|
||||||
|
const selectedCredits = ref(0)
|
||||||
|
const selectedCount = ref(0)
|
||||||
|
|
||||||
|
// 筛选相关数据
|
||||||
|
const tabs = ref(['全部课程', '专业选修', '公共选修', '通识教育'])
|
||||||
|
const activeTab = ref(0)
|
||||||
|
const semesters = ref(['2023-2024学年 第一学期', '2022-2023学年 第二学期', '2022-2023学年 第一学期'])
|
||||||
|
const currentSemester = ref('2023-2024学年 第一学期')
|
||||||
|
const categories = ref([
|
||||||
|
{ id: 1, name: '全部类别' },
|
||||||
|
{ id: 2, name: '人文社科' },
|
||||||
|
{ id: 3, name: '自然科学' },
|
||||||
|
{ id: 4, name: '工程技术' },
|
||||||
|
{ id: 5, name: '艺术体育' }
|
||||||
|
])
|
||||||
|
const currentCategory = ref({})
|
||||||
|
|
||||||
|
// 课程数据
|
||||||
|
const courses = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '人工智能基础',
|
||||||
|
credit: 3,
|
||||||
|
teacher: '张教授',
|
||||||
|
time: '周一 3-4节',
|
||||||
|
location: '信息楼201',
|
||||||
|
remaining: 15,
|
||||||
|
capacity: 60,
|
||||||
|
category: '工程技术',
|
||||||
|
type: '专业选修',
|
||||||
|
selected: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '数据可视化',
|
||||||
|
credit: 2,
|
||||||
|
teacher: '李副教授',
|
||||||
|
time: '周三 5-6节',
|
||||||
|
location: '计算机中心305',
|
||||||
|
remaining: 8,
|
||||||
|
capacity: 40,
|
||||||
|
category: '工程技术',
|
||||||
|
type: '专业选修',
|
||||||
|
selected: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '西方艺术史',
|
||||||
|
credit: 2,
|
||||||
|
teacher: '王教授',
|
||||||
|
time: '周二 7-8节',
|
||||||
|
location: '人文楼101',
|
||||||
|
remaining: 5,
|
||||||
|
capacity: 80,
|
||||||
|
category: '人文社科',
|
||||||
|
type: '公共选修',
|
||||||
|
selected: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: '环境科学导论',
|
||||||
|
credit: 2,
|
||||||
|
teacher: '赵教授',
|
||||||
|
time: '周四 1-2节',
|
||||||
|
location: '理学院203',
|
||||||
|
remaining: 12,
|
||||||
|
capacity: 100,
|
||||||
|
category: '自然科学',
|
||||||
|
type: '通识教育',
|
||||||
|
selected: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: '羽毛球',
|
||||||
|
credit: 1,
|
||||||
|
teacher: '陈老师',
|
||||||
|
time: '周五 5-6节',
|
||||||
|
location: '体育馆A',
|
||||||
|
remaining: 3,
|
||||||
|
capacity: 30,
|
||||||
|
category: '艺术体育',
|
||||||
|
type: '公共选修',
|
||||||
|
selected: false
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const selectionProgress = computed(() => {
|
||||||
|
return Math.round((selectedCredits.value / availableCredits.value) * 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredCourses = computed(() => {
|
||||||
|
let result = [...courses.value]
|
||||||
|
|
||||||
|
// 按标签筛选
|
||||||
|
if (activeTab.value > 0) {
|
||||||
|
result = result.filter(course => course.type === tabs.value[activeTab.value])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按类别筛选
|
||||||
|
if (currentCategory.value.id > 1) {
|
||||||
|
result = result.filter(course => course.category === currentCategory.value.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const changeTab = (index) => {
|
||||||
|
activeTab.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSemesterChange = (e) => {
|
||||||
|
currentSemester.value = semesters.value[e.detail.value]
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCategoryChange = (e) => {
|
||||||
|
currentCategory.value = categories.value[e.detail.value]
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleSelection = (course) => {
|
||||||
|
if (course.selected) {
|
||||||
|
// 取消选课
|
||||||
|
course.selected = false
|
||||||
|
selectedCredits.value -= course.credit
|
||||||
|
selectedCount.value--
|
||||||
|
course.remaining++
|
||||||
|
} else {
|
||||||
|
// 检查学分是否足够
|
||||||
|
if (selectedCredits.value + course.credit > availableCredits.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '已超过可选学分限制',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否还有名额
|
||||||
|
if (course.remaining <= 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '该课程已无剩余名额',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选课
|
||||||
|
course.selected = true
|
||||||
|
selectedCredits.value += course.credit
|
||||||
|
selectedCount.value++
|
||||||
|
course.remaining--
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '选课成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (selectedCount.value === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请至少选择一门课程',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showLoading({ title: '提交中...' })
|
||||||
|
|
||||||
|
// 模拟API请求
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '选课提交成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重置选课状态
|
||||||
|
courses.value.forEach(course => {
|
||||||
|
if (course.selected) {
|
||||||
|
course.capacity--
|
||||||
|
course.selected = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
selectedCredits.value = 0
|
||||||
|
selectedCount.value = 0
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.selection-page {
|
||||||
|
padding-bottom: 120rpx;
|
||||||
|
background: linear-gradient(to bottom, #0a0e17 0%, #1a2a3a 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-section {
|
||||||
|
position: relative;
|
||||||
|
padding: 40rpx 30rpx 30rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 42rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-decoration {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-cards {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.status-card {
|
||||||
|
flex: 1;
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 25rpx;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.card-label {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-value {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
display: block;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-unit {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-decoration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -20rpx;
|
||||||
|
right: -20rpx;
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-section {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #4facfe;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-decoration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -8rpx;
|
||||||
|
left: 0;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-section {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.filter-tabs {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
border-bottom: 1rpx solid rgba(74, 144, 226, 0.2);
|
||||||
|
|
||||||
|
.filter-tab {
|
||||||
|
padding: 15rpx 25rpx;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-indicator {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -1rpx;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 0;
|
||||||
|
height: 4rpx;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
border-radius: 2rpx;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
text {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-indicator {
|
||||||
|
width: 60rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-options {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
.filter-option {
|
||||||
|
flex: 1;
|
||||||
|
height: 70rpx;
|
||||||
|
line-height: 70rpx;
|
||||||
|
padding: 0 25rpx;
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 26rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-list {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
|
||||||
|
.course-card {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 25rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
.course-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.course-name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-credit {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #4facfe;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-meta {
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
|
||||||
|
.meta-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.remaining {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100rpx;
|
||||||
|
background: rgba(16, 24, 40, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-top: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
|
||||||
|
.selected-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.tech-button) {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,450 @@
|
|||||||
|
<template>
|
||||||
|
<view class="transcript-page">
|
||||||
|
<!-- 头部信息 -->
|
||||||
|
<view class="header-section">
|
||||||
|
<view class="header-content">
|
||||||
|
<text class="title">成绩单</text>
|
||||||
|
<text class="subtitle">Academic Transcript</text>
|
||||||
|
</view>
|
||||||
|
<view class="header-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 学期选择器 -->
|
||||||
|
<view class="semester-selector">
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="semesters"
|
||||||
|
range-key="name"
|
||||||
|
@change="handleSemesterChange"
|
||||||
|
>
|
||||||
|
<view class="selector-box">
|
||||||
|
<text class="current-semester">{{ currentSemester.name }}</text>
|
||||||
|
<uni-icons type="arrowdown" size="18" color="#4facfe"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- GPA统计卡片 -->
|
||||||
|
<view class="stats-grid">
|
||||||
|
<view class="stat-card">
|
||||||
|
<view class="stat-content">
|
||||||
|
<text class="stat-label">学期GPA</text>
|
||||||
|
<text class="stat-value">{{ currentSemester.gpa }}</text>
|
||||||
|
<view class="stat-trend" :class="gpaTrendClass">
|
||||||
|
<uni-icons :type="gpaTrendIcon" size="16" color="currentColor"></uni-icons>
|
||||||
|
<text>{{ gpaTrendText }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="stat-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="stat-card">
|
||||||
|
<view class="stat-content">
|
||||||
|
<text class="stat-label">累计GPA</text>
|
||||||
|
<text class="stat-value">{{ cumulativeGPA }}</text>
|
||||||
|
<text class="stat-meta">专业排名 {{ rank }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-decoration"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 成绩列表 -->
|
||||||
|
<view class="grades-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<text class="section-title">课程成绩</text>
|
||||||
|
<view class="section-decoration"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="grades-list">
|
||||||
|
<view
|
||||||
|
v-for="(course, index) in currentSemester.courses"
|
||||||
|
:key="index"
|
||||||
|
class="grade-item"
|
||||||
|
>
|
||||||
|
<view class="course-info">
|
||||||
|
<text class="course-name">{{ course.name }}</text>
|
||||||
|
<text class="course-credit">{{ course.credit }}学分</text>
|
||||||
|
</view>
|
||||||
|
<view class="grade-score" :class="getScoreClass(course.score)">
|
||||||
|
<text>{{ course.score }}</text>
|
||||||
|
<text class="grade-level">{{ getGradeLevel(course.score) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部操作按钮 -->
|
||||||
|
<view class="action-buttons">
|
||||||
|
<TechButton type="primary" @click="handleDownload">
|
||||||
|
<uni-icons type="download" size="18" color="#fff"></uni-icons>
|
||||||
|
<text>下载成绩单</text>
|
||||||
|
</TechButton>
|
||||||
|
<TechButton type="secondary" @click="handleShare">
|
||||||
|
<uni-icons type="share" size="18" color="#4facfe"></uni-icons>
|
||||||
|
<text>分享成绩</text>
|
||||||
|
</TechButton>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import TechButton from '@/components/managemet/TechButton.vue'
|
||||||
|
|
||||||
|
const semesters = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '2023-2024学年 第一学期',
|
||||||
|
gpa: '3.82',
|
||||||
|
trend: 'up',
|
||||||
|
courses: [
|
||||||
|
{ name: '人工智能原理', credit: 4, score: 94 },
|
||||||
|
{ name: '机器学习', credit: 4, score: 89 },
|
||||||
|
{ name: '大数据技术', credit: 3, score: 91 },
|
||||||
|
{ name: '计算机视觉', credit: 3, score: 87 },
|
||||||
|
{ name: '自然语言处理', credit: 3, score: 92 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '2022-2023学年 第二学期',
|
||||||
|
gpa: '3.75',
|
||||||
|
trend: 'up',
|
||||||
|
courses: [
|
||||||
|
{ name: '操作系统', credit: 4, score: 88 },
|
||||||
|
{ name: '计算机网络', credit: 4, score: 85 },
|
||||||
|
{ name: '数据库系统', credit: 4, score: 90 },
|
||||||
|
{ name: '软件工程', credit: 3, score: 87 },
|
||||||
|
{ name: '编译原理', credit: 4, score: 83 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '2022-2023学年 第一学期',
|
||||||
|
gpa: '3.68',
|
||||||
|
trend: 'down',
|
||||||
|
courses: [
|
||||||
|
{ name: '数据结构', credit: 4, score: 85 },
|
||||||
|
{ name: '算法分析', credit: 4, score: 82 },
|
||||||
|
{ name: '计算机组成', credit: 4, score: 84 },
|
||||||
|
{ name: '离散数学', credit: 3, score: 79 },
|
||||||
|
{ name: '概率统计', credit: 3, score: 88 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const currentSemesterIndex = ref(0)
|
||||||
|
const currentSemester = computed(() => semesters.value[currentSemesterIndex.value])
|
||||||
|
const cumulativeGPA = computed(() => '3.75')
|
||||||
|
const rank = computed(() => '8/120')
|
||||||
|
|
||||||
|
// GPA趋势相关计算
|
||||||
|
const gpaTrendIcon = computed(() => {
|
||||||
|
return currentSemester.value.trend === 'up' ? 'arrow-up' : 'arrow-down'
|
||||||
|
})
|
||||||
|
|
||||||
|
const gpaTrendClass = computed(() => {
|
||||||
|
return currentSemester.value.trend === 'up' ? 'trend-up' : 'trend-down'
|
||||||
|
})
|
||||||
|
|
||||||
|
const gpaTrendText = computed(() => {
|
||||||
|
return currentSemester.value.trend === 'up' ? '较上学期提升' : '较上学期下降'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 成绩等级计算
|
||||||
|
const getGradeLevel = (score) => {
|
||||||
|
if (score >= 90) return 'A'
|
||||||
|
if (score >= 80) return 'B'
|
||||||
|
if (score >= 70) return 'C'
|
||||||
|
if (score >= 60) return 'D'
|
||||||
|
return 'F'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getScoreClass = (score) => {
|
||||||
|
if (score >= 90) return 'excellent'
|
||||||
|
if (score >= 80) return 'good'
|
||||||
|
if (score >= 70) return 'average'
|
||||||
|
if (score >= 60) return 'pass'
|
||||||
|
return 'fail'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件处理
|
||||||
|
const handleSemesterChange = (e) => {
|
||||||
|
currentSemesterIndex.value = e.detail.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDownload = () => {
|
||||||
|
uni.showLoading({ title: '生成成绩单中...' })
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '成绩单已保存到相册',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleShare = () => {
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['微信好友', 'QQ好友', '保存图片'],
|
||||||
|
success: (res) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: `已分享到${['微信好友', 'QQ好友', '保存图片'][res.tapIndex]}`,
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.transcript-page {
|
||||||
|
padding-bottom: 40rpx;
|
||||||
|
background: linear-gradient(to bottom, #0a0e17 0%, #1a2a3a 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-section {
|
||||||
|
position: relative;
|
||||||
|
padding: 40rpx 30rpx 30rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 42rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-decoration {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.semester-selector {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
|
.selector-box {
|
||||||
|
height: 80rpx;
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 25rpx;
|
||||||
|
|
||||||
|
.current-semester {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 25rpx;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
.stat-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-trend {
|
||||||
|
font-size: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
|
||||||
|
&.trend-up {
|
||||||
|
color: #00c853;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.trend-down {
|
||||||
|
color: #ff5252;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-meta {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-decoration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -20rpx;
|
||||||
|
right: -20rpx;
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grades-section {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
margin-bottom: 25rpx;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #4facfe;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
padding-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-decoration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -8rpx;
|
||||||
|
left: 0;
|
||||||
|
width: 60rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
background: linear-gradient(to right, #00f2fe, #4facfe);
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grades-list {
|
||||||
|
.grade-item {
|
||||||
|
background: rgba(16, 24, 40, 0.8);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 25rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border: 1rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
|
.course-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.course-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-credit {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-score {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&.excellent {
|
||||||
|
background: rgba(0, 200, 83, 0.15);
|
||||||
|
border: 2rpx solid rgba(0, 200, 83, 0.3);
|
||||||
|
color: #00c853;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.good {
|
||||||
|
background: rgba(74, 144, 226, 0.15);
|
||||||
|
border: 2rpx solid rgba(74, 144, 226, 0.3);
|
||||||
|
color: #4facfe;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.average {
|
||||||
|
background: rgba(255, 171, 0, 0.15);
|
||||||
|
border: 2rpx solid rgba(255, 171, 0, 0.3);
|
||||||
|
color: #ffab00;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pass {
|
||||||
|
background: rgba(255, 82, 82, 0.15);
|
||||||
|
border: 2rpx solid rgba(255, 82, 82, 0.3);
|
||||||
|
color: #ff5252;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fail {
|
||||||
|
background: rgba(255, 82, 82, 0.3);
|
||||||
|
border: 2rpx solid rgba(255, 82, 82, 0.5);
|
||||||
|
color: #ff5252;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-level {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-top: 5rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
|
||||||
|
:deep(.tech-button) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<text class="title">培养方案</text>
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
// 这里可以添加页面逻辑
|
||||||
|
const pageTitle = ref('培养方案')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view>课程答题</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<view class="role-select-page">
|
||||||
|
<view class="title">请选择您的身份</view>
|
||||||
|
<view class="role-buttons">
|
||||||
|
<button @click="selectStudent">学生</button>
|
||||||
|
<button @click="selectTeacher">教师</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { uni } from '@dcloudio/uni-app';
|
||||||
|
|
||||||
|
const selectStudent = () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/Student/learning/learning'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectTeacher = () => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/Teacher/teacherCourseManagement/teacherCourseManagement'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.role-select-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,433 @@
|
|||||||
|
<template>
|
||||||
|
<view class="cyber-container">
|
||||||
|
<!-- 霓虹风格顶部栏 -->
|
||||||
|
<view class="cyber-header">
|
||||||
|
<view class="header-glitch" data-text="智能课程表">智能课程表</view>
|
||||||
|
<view class="week-display">
|
||||||
|
<text class="week-label">当前周次</text>
|
||||||
|
<text class="week-value">{{ currentWeek }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 全息投影风格课表 -->
|
||||||
|
<view class="hologram-grid">
|
||||||
|
<!-- 星期表头 -->
|
||||||
|
<view class="grid-header">
|
||||||
|
<view class="time-slot">时间</view>
|
||||||
|
<view
|
||||||
|
v-for="day in weekDays"
|
||||||
|
:key="day.value"
|
||||||
|
class="day-header"
|
||||||
|
:class="{ today: isToday(day.value) }"
|
||||||
|
>
|
||||||
|
{{ day.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 课程行 -->
|
||||||
|
<view
|
||||||
|
v-for="(timeSlot, slotIndex) in timeSlots"
|
||||||
|
:key="slotIndex"
|
||||||
|
class="grid-row"
|
||||||
|
>
|
||||||
|
<view class="time-slot">
|
||||||
|
<text class="time">{{ timeSlot }}</text>
|
||||||
|
<text class="slot-num">第{{ slotIndex + 1 }}节</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- <view>测试{{ coursesData.mon[0].name }}</view> -->
|
||||||
|
|
||||||
|
<view
|
||||||
|
v-for="day in weekDays"
|
||||||
|
:key="day.value"
|
||||||
|
class="course-slot"
|
||||||
|
@click="showCourseDetail(day.value, slotIndex)"
|
||||||
|
>
|
||||||
|
<!-- <course-hologram
|
||||||
|
v-if="hasCourse(day.value, slotIndex)"
|
||||||
|
:course="getCourse(day.value, slotIndex)"
|
||||||
|
:active="isCurrentWeekCourse(day.value, slotIndex)"
|
||||||
|
/> -->
|
||||||
|
<course-hologram
|
||||||
|
v-if="hasCourse(day.value, slotIndex)"
|
||||||
|
:course="getCourse(day.value, slotIndex)"
|
||||||
|
:active="isCurrentWeekCourse(day.value, slotIndex)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 全息弹窗 -->
|
||||||
|
<uni-popup ref="popup" type="dialog">
|
||||||
|
<uni-popup-dialog
|
||||||
|
v-if="currentCourse"
|
||||||
|
:title="currentCourse.name"
|
||||||
|
:message="courseDetail"
|
||||||
|
confirmText="确定"
|
||||||
|
@confirm="closePopup"
|
||||||
|
/>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import 'dayjs/locale/zh-cn'
|
||||||
|
// 初始化dayjs中文
|
||||||
|
dayjs.locale('zh-cn')
|
||||||
|
|
||||||
|
// 霓虹颜色方案
|
||||||
|
const COLOR_SCHEME = {
|
||||||
|
math: '#00ff9d',
|
||||||
|
programming: '#00b8ff',
|
||||||
|
english: '#ff2d75',
|
||||||
|
physics: '#ff9d00',
|
||||||
|
default: '#8a2be2'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 星期数据
|
||||||
|
const weekDays = [
|
||||||
|
{ label: '周一', value: 'mon' },
|
||||||
|
{ label: '周二', value: 'tue' },
|
||||||
|
{ label: '周三', value: 'wed' },
|
||||||
|
{ label: '周四', value: 'thu' },
|
||||||
|
{ label: '周五', value: 'fri' },
|
||||||
|
{ label: '周六', value: 'sat' },
|
||||||
|
{ label: '周日', value: 'sun' }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 时间配置
|
||||||
|
const timeSlots = [
|
||||||
|
'08:20-09:05', '09:10-09:55', '10:15-11:00',
|
||||||
|
'11:05-11:50', '14:00-14:45', '14:50-15:35',
|
||||||
|
'15:55-16:40', '16:45-17:30', '18:30-19:15',
|
||||||
|
'19:20-20:05', '20:10-20:55', '21:00-21:45'
|
||||||
|
]
|
||||||
|
|
||||||
|
// 课程数据
|
||||||
|
const coursesData = {
|
||||||
|
mon: [
|
||||||
|
{
|
||||||
|
section: 0,
|
||||||
|
name: '高等数学A(一)',
|
||||||
|
teacher: '李教授',
|
||||||
|
location: 'A101',
|
||||||
|
weeks: '1-16',
|
||||||
|
type: 'math'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
section: 1,
|
||||||
|
name: '高等数学A(一)',
|
||||||
|
teacher: '李教授',
|
||||||
|
location: 'A101',
|
||||||
|
weeks: '1-19',
|
||||||
|
type: 'math'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tue: [
|
||||||
|
{
|
||||||
|
section: 3,
|
||||||
|
name: '程序设计基础',
|
||||||
|
teacher: '王老师',
|
||||||
|
location: '计科实验室',
|
||||||
|
weeks: '1-12',
|
||||||
|
type: 'programming'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
wed: [
|
||||||
|
{
|
||||||
|
section: 2,
|
||||||
|
name: '大学英语',
|
||||||
|
teacher: '张老师',
|
||||||
|
location: 'C203',
|
||||||
|
weeks: '1-18',
|
||||||
|
type: 'english'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const currentWeek = ref(1)
|
||||||
|
const currentCourse = ref(null)
|
||||||
|
const popup = ref(null)
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const courseDetail = computed(() => {
|
||||||
|
if (!currentCourse.value) return ''
|
||||||
|
return `教师: ${currentCourse.value.teacher}\n地点: ${currentCourse.value.location}\n周次: ${currentCourse.value.weeks}周`
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算当前周次
|
||||||
|
const calculateCurrentWeek = () => {
|
||||||
|
const startDate = dayjs('2025-03-1') // 开学日期
|
||||||
|
const weeks = dayjs().diff(startDate, 'week') + 1
|
||||||
|
currentWeek.value = Math.max(1, Math.min(weeks, 18)) // 限制在1-18周
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否是今天
|
||||||
|
const isToday = (dayValue) => {
|
||||||
|
const dayMap = { mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6, sun: 0 }
|
||||||
|
return dayMap[dayValue] === dayjs().day()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查某天某节是否有课
|
||||||
|
const hasCourse = (day, section) => {
|
||||||
|
return coursesData[day]?.some(c => c.section === section) ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取课程
|
||||||
|
const getCourse = (day, section) => {
|
||||||
|
return coursesData[day]?.find(c => c.section === section) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否当前周课程
|
||||||
|
const isCurrentWeekCourse = (day, section) => {
|
||||||
|
const course = getCourse(day, section)
|
||||||
|
if (!course) return false
|
||||||
|
|
||||||
|
const weeks = course.weeks
|
||||||
|
if (weeks.includes('-')) {
|
||||||
|
const [start, end] = weeks.split('-').map(Number)
|
||||||
|
return currentWeek.value >= start && currentWeek.value <= end
|
||||||
|
} else if (weeks.includes(',')) {
|
||||||
|
return weeks.split(',').map(Number).includes(currentWeek.value)
|
||||||
|
}
|
||||||
|
return Number(weeks) === currentWeek.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示课程详情
|
||||||
|
const showCourseDetail = (day, section) => {
|
||||||
|
currentCourse.value = getCourse(day, section)
|
||||||
|
if (currentCourse.value) {
|
||||||
|
popup.value.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closePopup = () => {
|
||||||
|
popup.value.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件生命周期
|
||||||
|
onMounted(() => {
|
||||||
|
calculateCurrentWeek()
|
||||||
|
})
|
||||||
|
|
||||||
|
//课程卡片组件
|
||||||
|
const CourseHologram = {
|
||||||
|
props: ['course', 'active'],
|
||||||
|
computed: {
|
||||||
|
courseColor() {
|
||||||
|
return COLOR_SCHEME[this.course.type] || COLOR_SCHEME.default
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<view
|
||||||
|
class="hologram-card"
|
||||||
|
:style="{
|
||||||
|
'--hologram-color': courseColor,
|
||||||
|
opacity: active ? 1 : 0.6
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<text class="course-name">{{ course.name }}</text>
|
||||||
|
<text class="course-location">{{ course.location }}</text>
|
||||||
|
<view class="hologram-effect"></view>
|
||||||
|
</view>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.cyber-container {
|
||||||
|
background: #0a0e17;
|
||||||
|
color: #00ff9d;
|
||||||
|
min-height: 100vh;
|
||||||
|
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cyber-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
background: rgba(0, 20, 40, 0.8);
|
||||||
|
border-bottom: 1px solid rgba(0, 255, 157, 0.3);
|
||||||
|
|
||||||
|
.header-glitch {
|
||||||
|
position: relative;
|
||||||
|
font-size: 44rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #00ff9d;
|
||||||
|
text-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.week-display {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.week-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
color: rgba(0, 255, 157, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.week-value {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #00ff9d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-grid {
|
||||||
|
margin: 20rpx;
|
||||||
|
border: 1px solid rgba(0, 255, 157, 0.2);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.grid-header {
|
||||||
|
display: flex;
|
||||||
|
background: rgba(0, 30, 60, 0.8);
|
||||||
|
|
||||||
|
.time-slot {
|
||||||
|
width: 160rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid rgba(0, 255, 157, 0.2);
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-header {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid rgba(0, 255, 157, 0.2);
|
||||||
|
font-size: 28rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.today {
|
||||||
|
color: #00ff9d;
|
||||||
|
position: relative;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10rpx;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 40rpx;
|
||||||
|
height: 3rpx;
|
||||||
|
background: #00ff9d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-row {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid rgba(0, 255, 157, 0.1);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-slot {
|
||||||
|
width: 160rpx;
|
||||||
|
padding: 15rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-right: 1px solid rgba(0, 255, 157, 0.1);
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #00b8ff;
|
||||||
|
|
||||||
|
.slot-num {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: rgba(0, 184, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-slot {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 120rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
border-right: 1px solid rgba(0, 255, 157, 0.1);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-card {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
padding: 15rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
background: rgba(var(--hologram-color-rgb), 0.1);
|
||||||
|
border: 1px solid var(--hologram-color);
|
||||||
|
color: white;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.course-name {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-location {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-effect {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
rgba(var(--hologram-color-rgb), 0.1) 0%,
|
||||||
|
rgba(var(--hologram-color-rgb), 0.3) 50%,
|
||||||
|
rgba(var(--hologram-color-rgb), 0.1) 100%
|
||||||
|
);
|
||||||
|
opacity: 0.5;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 颜色变量转换 */
|
||||||
|
.hologram-card {
|
||||||
|
--hologram-color-rgb: 0, 255, 157;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-card[style*="math"] {
|
||||||
|
--hologram-color-rgb: 0, 255, 157;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-card[style*="programming"] {
|
||||||
|
--hologram-color-rgb: 0, 184, 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-card[style*="english"] {
|
||||||
|
--hologram-color-rgb: 255, 45, 117;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-card[style*="physics"] {
|
||||||
|
--hologram-color-rgb: 255, 157, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hologram-card[style*="default"] {
|
||||||
|
--hologram-color-rgb: 138, 43, 226;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<text class="title">成绩</text>
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
// 这里可以添加页面逻辑
|
||||||
|
const pageTitle = ref('成绩')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<text class="title">设置</text>
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref
|
||||||
|
} from 'vue'
|
||||||
|
|
||||||
|
// 这里可以添加页面逻辑
|
||||||
|
const pageTitle = ref('设置')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
<h3 >
|
||||||
|
上课
|
||||||
|
</h3>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
// 脚本逻辑
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script >
|
||||||
|
import io from '@hyoga/uni-socket.io'
|
||||||
|
export default {
|
||||||
|
onLoad(options) {
|
||||||
|
// 初始化连接
|
||||||
|
console.log(options)
|
||||||
|
this.socket = io('ws://localhost:3400', { transports: ['websocket'],auth:{
|
||||||
|
xuehao:"202413501062",
|
||||||
|
sf:"stu",
|
||||||
|
kch:options.kch,
|
||||||
|
kctime:options.kctime,
|
||||||
|
k_id:options.uuid,
|
||||||
|
} })
|
||||||
|
// 监听事件
|
||||||
|
|
||||||
|
this.socket.on('connect', () => console.log('Socket 已连接'))
|
||||||
|
this.socket.on('server', data => {
|
||||||
|
console.log(data)
|
||||||
|
})
|
||||||
|
this.socket.on('message', data => this.handleMessage(data))
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
// 断开连接并移除监听
|
||||||
|
this.socket.off('message')
|
||||||
|
this.socket.disconnect()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleMessage(data) { /* 处理消息 */ },
|
||||||
|
sendData() {
|
||||||
|
this.socket.emit('chat', { text: 'Hello' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,376 @@
|
|||||||
|
<template>
|
||||||
|
<view class="tech-container">
|
||||||
|
<!-- 科技风背景元素 -->
|
||||||
|
<view class="tech-bg">
|
||||||
|
<view class="tech-grid"></view>
|
||||||
|
<view class="tech-circle circle-1"></view>
|
||||||
|
<view class="tech-circle circle-2"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 用户信息卡片 -->
|
||||||
|
<view class="tech-card user-card">
|
||||||
|
<view class="user-info">
|
||||||
|
<view class="avatar-wrapper" @click="chooseAvatar">
|
||||||
|
<image class="avatar" :src="userInfo.avatar || '/static/user/avatar.png'" mode="aspectFill"></image>
|
||||||
|
<view class="avatar-border"></view>
|
||||||
|
</view>
|
||||||
|
<view class="info-right">
|
||||||
|
<view class="name-line" @click="editName">
|
||||||
|
<text class="username">{{ userInfo.name || '张三' }}</text>
|
||||||
|
<uni-icons type="compose" size="18" color="#00f0ff"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="tech-input-line" @click="editStudentId">
|
||||||
|
<text class="tech-label">学号:</text>
|
||||||
|
<text class="student-id">{{ userInfo.studentId || '20230001' }}</text>
|
||||||
|
<uni-icons type="arrowright" size="18" color="#7f8fa6"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="tech-divider"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 功能卡片 -->
|
||||||
|
<view class="tech-card function-card">
|
||||||
|
<view class="function-grid">
|
||||||
|
<view class="function-item" v-for="item in functionList" :key="item.type" @click="navigateTo(item.type)">
|
||||||
|
<view class="tech-icon-wrapper">
|
||||||
|
<image class="function-icon" :src="item.icon"></image>
|
||||||
|
<view class="icon-halo"></view>
|
||||||
|
</view>
|
||||||
|
<text class="function-text">{{ item.name }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
|
||||||
|
// 用户信息
|
||||||
|
const userInfo = ref({
|
||||||
|
name: '张三',
|
||||||
|
studentId: '20230001',
|
||||||
|
avatar: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 功能列表
|
||||||
|
const functionList = ref([
|
||||||
|
{ type: 'leave', name: '请假', icon: '/static/user/leave.png' },
|
||||||
|
{ type: 'plan', name: '培养方案', icon: '/static/user/plan.png' },
|
||||||
|
{ type: 'course', name: '选课', icon: '/static/user/course.png' },
|
||||||
|
{ type: 'score', name: '成绩', icon: '/static/user/score.png' },
|
||||||
|
{ type: 'settings', name: '设置', icon: '/static/user/settings.png' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 导航函数
|
||||||
|
const navigateTo = (type) => {
|
||||||
|
const pages = {
|
||||||
|
'leave': '/pages/leave/leave',
|
||||||
|
'plan': '/pages/plan/plan',
|
||||||
|
'course': '/pages/course/course',
|
||||||
|
'score': '/pages/score/score',
|
||||||
|
'settings': '/pages/settings/settings'
|
||||||
|
}
|
||||||
|
uni.navigateTo({ url: pages[type] })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择头像
|
||||||
|
const chooseAvatar = () => {
|
||||||
|
uni.chooseImage({
|
||||||
|
count: 1,
|
||||||
|
sizeType: ['compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success: (res) => {
|
||||||
|
userInfo.value.avatar = res.tempFilePaths[0]
|
||||||
|
saveUserInfo()
|
||||||
|
uni.showToast({ title: '头像更新成功', icon: 'success' })
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: '取消选择', icon: 'none' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑姓名
|
||||||
|
const editName = () => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '修改姓名',
|
||||||
|
content: userInfo.value.name,
|
||||||
|
editable: true,
|
||||||
|
placeholderText: '请输入姓名',
|
||||||
|
confirmText: '保存',
|
||||||
|
cancelText: '取消',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm && res.content) {
|
||||||
|
userInfo.value.name = res.content.trim()
|
||||||
|
saveUserInfo()
|
||||||
|
uni.showToast({ title: '姓名修改成功', icon: 'success' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑学号
|
||||||
|
const editStudentId = () => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '修改学号',
|
||||||
|
content: userInfo.value.studentId,
|
||||||
|
editable: true,
|
||||||
|
placeholderText: '请输入学号',
|
||||||
|
confirmText: '保存',
|
||||||
|
cancelText: '取消',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm && res.content) {
|
||||||
|
userInfo.value.studentId = res.content.trim()
|
||||||
|
saveUserInfo()
|
||||||
|
uni.showToast({ title: '学号修改成功', icon: 'success' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存用户信息
|
||||||
|
const saveUserInfo = () => {
|
||||||
|
uni.setStorageSync('userInfo', userInfo.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化数据
|
||||||
|
onLoad(() => {
|
||||||
|
const cachedUserInfo = uni.getStorageSync('userInfo')
|
||||||
|
if (cachedUserInfo) {
|
||||||
|
userInfo.value = cachedUserInfo
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tech-container {
|
||||||
|
padding: 30rpx;
|
||||||
|
background: linear-gradient(135deg, #0a0e21 0%, #121a3a 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 科技风背景 */
|
||||||
|
.tech-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: -1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.tech-grid {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(rgba(0, 240, 255, 0.05) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(0, 240, 255, 0.05) 1px, transparent 1px);
|
||||||
|
background-size: 40rpx 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-circle {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(0, 240, 255, 0.05), transparent 70%);
|
||||||
|
|
||||||
|
&.circle-1 {
|
||||||
|
width: 400rpx;
|
||||||
|
height: 400rpx;
|
||||||
|
top: -200rpx;
|
||||||
|
right: -200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.circle-2 {
|
||||||
|
width: 300rpx;
|
||||||
|
height: 300rpx;
|
||||||
|
bottom: -150rpx;
|
||||||
|
left: -150rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-card {
|
||||||
|
background: rgba(30, 41, 59, 0.6);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1rpx solid rgba(0, 240, 255, 0.1);
|
||||||
|
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2rpx;
|
||||||
|
background: linear-gradient(90deg, transparent, #00f0ff, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card {
|
||||||
|
padding: 40rpx;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 140rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
margin-right: 30rpx;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-border {
|
||||||
|
position: absolute;
|
||||||
|
top: -4rpx;
|
||||||
|
left: -4rpx;
|
||||||
|
right: -4rpx;
|
||||||
|
bottom: -4rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid #00f0ff;
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-right {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.name-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.username {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
background: linear-gradient(to right, #00f0ff, #0088ff);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-input-line {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
|
||||||
|
.tech-label {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #7f8fa6;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-id {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #fff;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-divider {
|
||||||
|
height: 2rpx;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.3), transparent);
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-card {
|
||||||
|
padding: 30rpx;
|
||||||
|
|
||||||
|
.function-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.function-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 30rpx 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:not(:last-child)::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1rpx;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.1), transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-icon-wrapper {
|
||||||
|
width: 90rpx;
|
||||||
|
height: 90rpx;
|
||||||
|
margin-right: 30rpx;
|
||||||
|
background: rgba(0, 240, 255, 0.1);
|
||||||
|
border-radius: 22rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
border: 1rpx solid rgba(0, 240, 255, 0.2);
|
||||||
|
|
||||||
|
.function-icon {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-halo {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 22rpx;
|
||||||
|
background: radial-gradient(circle, rgba(0, 240, 255, 0.3), transparent 70%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.function-text {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #fff;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
.tech-icon-wrapper .icon-halo {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画 */
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.7;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,6 @@
|
|||||||
|
export {}
|
||||||
|
|
||||||
|
declare module "vue" {
|
||||||
|
type Hooks = App.AppInstance & Page.PageInstance;
|
||||||
|
interface ComponentCustomOptions extends Hooks {}
|
||||||
|
}
|
After Width: | Height: | Size: 677 B |
After Width: | Height: | Size: 636 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 976 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.0 KiB |
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"sourceMap": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"types": ["@dcloudio/types"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
// vite.config.ts
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import uni from '@dcloudio/vite-plugin-uni'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [uni()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, 'src'),
|
||||||
|
'/static': path.resolve(__dirname, 'src/static') // 关键配置
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|