Merge pull request #72 from Unclesimonlau/main

重新设计了移动端web页面,新增了移动端CSS,增加移动端SD绘图页面
This commit is contained in:
RockYang 2023-12-29 09:39:33 +08:00 committed by GitHub
commit df0e7508db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 3080 additions and 320 deletions

View File

@ -0,0 +1,13 @@
.custom-scroll ::-webkit-scrollbar {
width: 8px; /* 滚动条宽度 */
}
.custom-scroll ::-webkit-scrollbar-track {
background-color: #282c34;
}
.custom-scroll ::-webkit-scrollbar-thumb {
background-color: #444;
border-radius: 8px;
}
.custom-scroll ::-webkit-scrollbar-thumb:hover {
background-color: #666;
}

View File

@ -0,0 +1,26 @@
.custom-scroll {
/* */
::-webkit-scrollbar {
width: 8px; /* */
}
/* */
::-webkit-scrollbar-track {
background-color: #282C34;
}
/* */
::-webkit-scrollbar-thumb {
background-color: #444444;
border-radius 8px
}
/* */
::-webkit-scrollbar-thumb:hover {
background-color: #666666;
}
}

View File

@ -299,7 +299,6 @@
overflow: hidden; overflow: hidden;
border-radius: 6px; border-radius: 6px;
transition: all 0.3s ease; /* 添加过渡效果 */ transition: all 0.3s ease; /* 添加过渡效果 */
position: relative;
} }
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line { .page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
margin: 6px 0; margin: 6px 0;
@ -328,15 +327,6 @@
font-size: 20px; font-size: 20px;
cursor: pointer; cursor: pointer;
} }
.page-mj .inner .task-list-box .finish-job-list .job-item .remove {
display: none;
position: absolute;
right: 10px;
top: 10px;
}
.page-mj .inner .task-list-box .finish-job-list .job-item:hover .remove {
display: block;
}
.page-mj .inner .task-list-box .finish-job-list .animate:hover { .page-mj .inner .task-list-box .finish-job-list .animate:hover {
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */ box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
transform: translateY(-10px); /* 向上移动10像素 */ transform: translateY(-10px); /* 向上移动10像素 */

View File

@ -0,0 +1,252 @@
.page-sd {
background-color: #282828;
}
.page-sd .inner {
display: flex;
}
.page-sd .inner .sd-box {
margin: 10px;
background-color: #282828;
min-width: 92%;
max-width: 92%;
padding: 0px;
border-radius: 0px;
color: #fff;
font-size: 14px;
}
.page-sd .inner .sd-box h2 {
font-weight: bold;
font-size: 20px;
text-align: center;
color: #fff;
}
.page-sd .inner .sd-box ::-webkit-scrollbar {
width: 0;
height: 0;
background-color: transparent;
}
.page-sd .inner .sd-box .sd-params {
margin-top: 10px;
overflow: auto;
}
.page-sd .inner .sd-box .sd-params .param-line {
padding: 0 10px;
}
.page-sd .inner .sd-box .sd-params .param-line .el-icon {
position: relative;
top: 3px;
}
.page-sd .inner .sd-box .sd-params .param-line .el-input__suffix-inner .el-icon {
top: 0;
}
.page-sd .inner .sd-box .sd-params .param-line .grid-content,
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner {
display: flex;
}
.page-sd .inner .sd-box .sd-params .param-line .grid-content .el-icon,
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner .el-icon {
margin-left: 10px;
margin-top: 2px;
}
.page-sd .inner .sd-box .sd-params .param-line.pt {
padding-top: 5px;
padding-bottom: 5px;
}
.page-sd .inner .sd-box .submit-btn {
padding: 10px 15px 0 15px;
text-align: center;
}
.page-sd .inner .sd-box .submit-btn .el-button {
width: 100%;
}
.page-sd .inner .sd-box .submit-btn .el-button span {
color: #2d3a4b;
}
.page-sd .inner .el-form .el-form-item__label {
color: #fff;
}
.page-sd .inner .task-list-box {
width: 100%;
padding: 10px;
color: #fff;
overflow-x: hidden;
}
.page-sd .inner .task-list-box .running-job-list .job-item {
width: 100%;
padding: 2px;
background-color: #555;
}
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner {
position: relative;
height: 100%;
overflow: hidden;
}
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
font-size: 20px;
color: #fff;
}
.page-sd .inner .task-list-box .finish-job-list .job-item {
width: 100%;
height: 100%;
border: 1px solid #666;
padding: 6px;
overflow: hidden;
border-radius: 6px;
transition: all 0.3s ease; /* 添加过渡效果 */
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
margin: 6px 0;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
display: flex;
flex-flow: row;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
margin-right: 6px;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
padding: 3px 0;
width: 40px;
text-align: center;
border-radius: 5px;
display: block;
cursor: pointer;
background-color: #4e5058;
color: #fff;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
background-color: #6d6f78;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
font-size: 20px;
cursor: pointer;
}
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
transform: translateY(-10px); /* 向上移动10像素 */
}
.page-sd .inner .task-list-box .el-image {
width: 100%;
height: 100%;
overflow: visible;
}
.page-sd .inner .task-list-box .el-image img {
height: 100%;
}
.page-sd .inner .task-list-box .el-image .el-image-viewer__wrapper img {
width: auto;
height: auto;
}
.page-sd .inner .task-list-box .el-image .image-slot {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
height: 100%;
min-height: 200px;
color: #fff;
height: 240px;
}
.page-sd .inner .task-list-box .el-image .image-slot .iconfont {
font-size: 50px;
margin-bottom: 10px;
}
.page-sd .inner .task-list-box .el-image.upscale {
max-height: 100%;
}
.page-sd .inner .task-list-box .el-image.upscale img {
height: 100%;
}
.page-sd .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
width: auto;
height: auto;
}
.page-sd .el-overlay-dialog .el-dialog {
background-color: #1a1b1e;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__header .el-dialog__title {
color: #f5f5f5;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body {
padding: 0 0 0 15px !important;
display: flex;
height: 100%;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row {
width: 100%;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container {
display: flex;
justify-content: center;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot {
display: flex;
height: 100vh;
align-items: center;
justify-content: center;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot .el-icon {
font-size: 60px;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info {
background-color: #25262b;
padding: 1rem 1.5rem;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line {
width: 100%;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt {
background-color: #35363b;
padding: 10px;
color: #999;
overflow: auto;
max-height: 100%;
min-height: 100%;
position: relative;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt .el-icon {
position: absolute;
right: 10px;
bottom: 10px;
cursor: pointer;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper {
margin-top: 10px;
display: flex;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper label {
display: flex;
width:100%;
color: #a5a5a5;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper .item-value {
display: flex;
width: 100%;
background-color: #35363b;
padding: 2px 5px;
border-radius: 5px;
color: #f5f5f5;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params {
padding: 20px 0 10px 0;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params .el-button {
width: 100%;
}
.page-sd .mj-list-item-prompt .el-icon {
margin-left: 10px;
cursor: pointer;
position: relative;
top: 2px;
}

View File

@ -0,0 +1,106 @@
.page-sd {
background-color: #282828;
.inner {
display: flex;
align-items: center;
justify-content: center;
.sd-box {
background-color #282828
min-width 92%
max-width 92%
padding 0px
border-radius 0px
color #ffffff;
font-size 14px
h2 {
font-weight: bold;
font-size 20px
text-align center
color #fff
}
//
::-webkit-scrollbar {
width: 0;
height: 0;
background-color: transparent;
}
.sd-params {
margin-top 10px
overflow auto
.param-line {
padding 0 10px
.el-icon {
position relative
top 3px
}
.el-input__suffix-inner {
.el-icon {
top 0
}
}
.grid-content
.form-item-inner {
display flex
.el-icon {
margin-left 10px
margin-top 2px
}
}
}
.param-line.pt {
padding-top 5px
padding-bottom 5px
}
}
.submit-btn {
padding 10px 15px 0 15px
text-align center
.el-button {
width 100%
span {
color #2D3A4B
}
}
}
}
.el-form {
.el-form-item__label {
color #ffffff
}
}
@import "task-mobile-list.styl"
}
@import "sd-task-mobile-dialog.styl"
.mj-list-item-prompt {
.el-icon {
margin-left 10px
cursor pointer
position relative
top 2px
}
}
}

View File

@ -0,0 +1,261 @@
.page-sd {
background-color: #1c1c1c;
}
.page-sd .inner {
display: flex;
}
.page-sd .inner .sd-box {
margin: 5px;
background-color: #1c1c1c;
min-width: 97%;
max-width: 97%;
color: #fff;
font-size: 14px;
}
.page-sd .inner .sd-box h2 {
font-weight: bold;
font-size: 16px;
text-align: center;
color: #fff;
}
.page-sd .inner .sd-box .sd-params {
margin-top: 5px;
overflow: auto;
}
.page-sd .inner .sd-box .sd-params .param-line {
padding: 0 10px;
}
.page-sd .inner .sd-box .sd-params .param-line .el-icon {
position: relative;
top: 3px;
}
.page-sd .inner .sd-box .sd-params .param-line .el-input__suffix-inner .el-icon {
top: 0;
}
.page-sd .inner .sd-box .sd-params .param-line .grid-content,
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner {
display: flex;
}
.page-sd .inner .sd-box .sd-params .param-line .grid-content .el-icon,
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner .el-icon {
margin-left: 10px;
margin-top: 2px;
}
.page-sd .inner .sd-box .sd-params .param-line.pt {
padding-top: 5px;
padding-bottom: 5px;
}
.page-sd .inner .sd-box .submit-btn {
padding: 10px 15px 10px 15px;
text-align: center;
}
.page-sd .inner .sd-box .submit-btn .el-button {
width: 100%;
}
.page-sd .inner .sd-box .submit-btn .el-button span {
color: #2d3a4b;
}
.page-sd .inner .el-form .el-form-item__label {
color: #fff;
}
.page-sd .inner .task-list-box {
width: 100%;
color: #fff;
overflow-x: hidden;
}
.page-sd .inner .task-list-box .running-job-list .job-item {
width: 100%;
padding: 2px;
background-color: #555;
}
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner {
position: relative;
height: 100%;
overflow: hidden;
}
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
font-size: 20px;
color: #fff;
}
.page-sd .inner .task-list-box .finish-job-list .job-item {
width: 100%;
height: 100%;
border: 1px solid #666;
padding: 3px;
overflow: hidden;
border-radius: 6px;
transition: all 0.3s ease; /* 添加过渡效果 */
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
margin: 6px 0;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
display: flex;
flex-flow: row;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
margin-right: 6px;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
padding: 3px 0;
width: 40px;
text-align: center;
border-radius: 5px;
display: block;
cursor: pointer;
background-color: #4e5058;
color: #fff;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
background-color: #6d6f78;
}
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
font-size: 20px;
cursor: pointer;
}
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
transform: translateY(-10px); /* 向上移动10像素 */
}
.page-sd .inner .task-list-box .el-image {
width: 100%;
height: 100%;
overflow: visible;
}
.page-sd .inner .task-list-box .el-image img {
height: 100%;
}
.page-sd .inner .task-list-box .el-image .el-image-viewer__wrapper img {
width: auto;
height: auto;
}
.page-sd .inner .task-list-box .el-image .image-slot {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
height: 100%;
min-height: 200px;
color: #fff;
height: 240px;
}
.page-sd .inner .task-list-box .el-image .image-slot .iconfont {
font-size: 50px;
margin-bottom: 10px;
}
.page-sd .inner .task-list-box .el-image.upscale {
max-height: 100%;
}
.page-sd .inner .task-list-box .el-image.upscale img {
height: 100%;
}
.page-sd .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
width: auto;
height: auto;
}
.page-sd .el-overlay-dialog .el-dialog {
background-color: #1a1b1e;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__header .el-dialog__title {
color: #f5f5f5;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body {
padding: 0 0 0 15px !important;
display: flex;
height: 100%;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row {
width: 100%;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container {
display: flex;
justify-content: center;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot {
display: flex;
height: 100vh;
align-items: center;
justify-content: center;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot .el-icon {
font-size: 60px;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info {
background-color: #25262b;
padding: 1rem 1.5rem;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line {
width: 100%;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt {
background-color: #35363b;
padding: 10px;
color: #999;
overflow: auto;
max-height: 100%;
min-height: 100%;
position: relative;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt .el-icon {
position: absolute;
right: 10px;
bottom: 10px;
cursor: pointer;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper {
margin-top: 10px;
display: flex;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper label {
display: flex;
width:100%;
color: #a5a5a5;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper .item-value {
display: flex;
width: 100%;
background-color: #35363b;
padding: 2px 5px;
border-radius: 5px;
color: #f5f5f5;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params {
padding: 20px 0 10px 0;
}
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params .el-button {
width: 100%;
}
.page-sd .mj-list-item-prompt .el-icon {
cursor: pointer;
position: relative;
top: 2px;
}
.mobile-dialog {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* 针对手机屏幕的样式 */
.mobile-form-row {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.mobile-form-col {
width: 100%;
max-width: 500px;
padding: 30px;
box-sizing: border-box;
}

View File

@ -0,0 +1,106 @@
.page-sd {
background-color: #282828;
.inner {
display: flex;
align-items: center;
justify-content: center;
.sd-box {
background-color #282828
min-width 92%
max-width 92%
padding 0px
border-radius 0px
color #ffffff;
font-size 14px
h2 {
font-weight: bold;
font-size 16px
text-align center
color #fff
}
//
::-webkit-scrollbar {
width: 0;
height: 0;
background-color: transparent;
}
.sd-params {
margin-top 10px
overflow auto
.param-line {
padding 0 10px
.el-icon {
position relative
top 3px
}
.el-input__suffix-inner {
.el-icon {
top 0
}
}
.grid-content
.form-item-inner {
display flex
.el-icon {
margin-left 10px
margin-top 2px
}
}
}
.param-line.pt {
padding-top 5px
padding-bottom 5px
}
}
.submit-btn {
padding 10px 15px 0 15px
text-align center
.el-button {
width 100%
span {
color #2D3A4B
}
}
}
}
.el-form {
.el-form-item__label {
color #ffffff
}
}
@import "task-mobile-list.styl"
}
@import "sd-task-mobile-dialog.styl"
.mj-list-item-prompt {
.el-icon {
margin-left 10px
cursor pointer
position relative
top 2px
}
}
}

View File

@ -184,7 +184,6 @@
overflow: hidden; overflow: hidden;
border-radius: 6px; border-radius: 6px;
transition: all 0.3s ease; /* 添加过渡效果 */ transition: all 0.3s ease; /* 添加过渡效果 */
position: relative;
} }
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line { .page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
margin: 6px 0; margin: 6px 0;
@ -213,15 +212,6 @@
font-size: 20px; font-size: 20px;
cursor: pointer; cursor: pointer;
} }
.page-sd .inner .task-list-box .finish-job-list .job-item .remove {
display: none;
position: absolute;
right: 10px;
top: 10px;
}
.page-sd .inner .task-list-box .finish-job-list .job-item:hover .remove {
display: block;
}
.page-sd .inner .task-list-box .finish-job-list .animate:hover { .page-sd .inner .task-list-box .finish-job-list .animate:hover {
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */ box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
transform: translateY(-10px); /* 向上移动10像素 */ transform: translateY(-10px); /* 向上移动10像素 */

View File

@ -0,0 +1,20 @@
.title {
color: #fff;
text-align: center;
font-size: 16px;
font-weight: bold;
}
.app-background {
background-color: #282828;
height: 100vh;
}
.mobile-setting {
.content {
padding-top: 60px;
.van-field__label {
width: 100px;
text-align: right;
}
}
}

View File

@ -0,0 +1,20 @@
.title {
color #fff
text-align center
font-size 16px
font-weight bold
}
.app-background {
background-color #282828
height 100vh
}
.mobile-setting {
.content {
padding-top 60px
.van-field__label {
width 100px
text-align right
}
}
}

View File

@ -0,0 +1,57 @@
.app-background {
background-color: #1c1c1c;
height: 100vh;
}
.mobile-chat-list {
background-color: #1c1c1c;
position: absolute;
top: 40px;
width: 100%;
}
.content {
.van-cell__value {
.chat-list-item {
display: flex;
font-size: 16;
color: #a3a3a3;
background-color: #1c1c1c;
.van-image {
min-width: 32px;
width: 32px;
height: 32px;
}
.van-ellipsis {
margin-top: 5px;
margin-left: 10px;
}
}
}
}
.van-picker-column {
.picker-option {
display: flex;
width: 100%;
padding: 0 10px;
.van-image {
width: 20px;
height: 20px;
margin-right: 5px;
}
}
}
.van-nav-bar {
.van-nav-bar__right {
.van-icon {
font-size: 20px;
}
}
}
.popup {
background-color: #1c1c1c;
}
.dialog {
background-color: #1c1c1c;
}
.field {
background-color: #1c1c1c;
}

View File

@ -0,0 +1,58 @@
$fontSize = 16px;
.app-background {
background-color: #1c1c1c;
height: 100vh;
}
.mobile-chat-list {
background-color: #1c1c1c;
position: absolute;
top: 40px;
width: 100%;
}
.content {
.van-cell__value {
.chat-list-item {
display flex
font-size $fontSize
color: #fff;
background-color: #1c1c1c;
.van-image {
min-width 32px
width 32px
height 32px
}
.van-ellipsis {
margin-top 5px;
margin-left 10px;
}
}
}
}
.van-picker-column {
.picker-option {
display flex
width 100%
padding 0 10px
.van-image {
width 20px;
height 20px;
margin-right 5px
}
}
}
.van-nav-bar {
.van-nav-bar__right {
.van-icon {
font-size 20px;
}
}
}
.popup {
background-color: #1c1c1c;
}
.dialog {
background-color: #1c1c1c;
}
.field {
background-color: #1c1c1c;
}

View File

@ -0,0 +1,52 @@
.app-background {
background-color: #1c1c1c;
height: 100vh;
}
body,
.mobile-chat,
.van-sticky,
.van-nav-bar,
.van-list,
.message-list-box
.van-cell,
.chat-box,
.van-cell-group,
.van-field {
background-color: #1c1c1c !important;
color: #fff !important;
}
.chat-box-wrapper {
position: fixed;
bottom: 0;
width: 100%;
background-color: #1c1c1c;
}
.icon-box .van-icon,
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title,
.mobile-chat .van-nav-bar__title .van-cell__title,
.mobile-chat .van-nav-bar__right .van-icon {
color: #fff !important;
}
.mobile-chat {
.van-nav-bar__title {
.van-dropdown-menu__title {
margin-right: 15px;
}
.van-cell__title {
text-align: left;
}
}
.chat-list-wrapper {
position: fixed;
top: 40px;
bottom: 60px;
width: 100vw;
overflow-y: scroll;
}
.van-nav-bar__right {
.van-icon {
font-size: 20px;
}
}
}

View File

@ -0,0 +1,51 @@
.app-background {
background-color #1c1c1c
height 100vh
}
body,
.mobile-chat,
.van-sticky,
.van-nav-bar,
.van-list,
.message-list-box
.van-cell,
.chat-box,
.van-cell-group,
.van-field {
background-color #1c1c1c !important
color #fff !important
}
.chat-box-wrapper {
position fixed
bottom 0
width 100%
background-color #1c1c1c
}
.icon-box .van-icon,
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title,
.mobile-chat .van-nav-bar__title .van-cell__title,
.mobile-chat .van-nav-bar__right .van-icon {
color #fff !important
}
.mobile-chat {
.van-nav-bar__title {
.van-dropdown-menu__title {
margin-right 15px
}
.van-cell__title {
text-align left
}
}
.chat-list-wrapper {
position fixed
top 50px
bottom 60px
width 100vw
overflow-y scroll
}
.van-nav-bar__right {
.van-icon {
font-size 20px
}
}
}

View File

@ -0,0 +1,45 @@
.my-tabbar {
background-color: #171717;
box-shadow: -3px 4px 16px #000;
box-sizing: border-box;
}
.banner {
display: flex;
justify-content: center;
align-items: center;
background-color: #171717;
border-bottom-right-radius: 10px;
box-shadow: 0 -4px 13px #000;
height: 40px;
width: 100%;
position: fixed;
z-index: 2; /* Add z-index */
}
.banner-title {
color: #2CC995;
font-size: 20px;
font-weight: bold;
text-shadow: 0 -4px 13px #000;
}
.mobile-home {
background-color: #171717;
}
.mobile-home .van-tabbar::before {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 50px;
background-color: #171717;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
opacity: 0;
transition: opacity 0.5s;
z-index: -1;
}
.mobile-home .van-tabbar.activeTab::before {
opacity: 1;
}
.router-view {
margin-bottom: 50px;
}

View File

@ -0,0 +1,45 @@
.my-tabbar {
background-color #171717
box-shadow -3px 4px 16px #000
box-sizing border-box
}
.banner {
display flex
justify-content center
align-items center
background-color #171717
border-bottom-right-radius 10px
box-shadow 0 -4px 13px #000
height 40px
width 100%
position fixed
z-index 2 /* Add z-index */
}
.banner-title {
color #2CC995
font-size 20px
font-weight bold
text-shadow 0 -4px 13px #000
}
.mobile-home {
background-color #171717
}
.mobile-home .van-tabbar before {
position absolute
bottom 0
left 0
right 0
height 50px
background-color #171717
border-top-left-radius 10px
border-top-right-radius 10px
opacity 0
transition opacity 0.5s
z-index -1
}
.mobile-home .van-tabbar.activeTab before {
opacity 1
}
.router-view {
margin-bottom 50px
}

View File

@ -0,0 +1,94 @@
.page-invitation {
display: flex;
justify-content: center;
background-color: #1c1c1c;
height: 100vh;
overflow-x: hidden;
overflow-y: visible;
.inner {
max-width: 100%;
width: 100%;
color: #fff;
.center {
display: flex;
justify-content: center;
align-items: center;
}
.title {
color: #fff;
text-align: center;
font-size: 16px;
font-weight: bold;
}
.share-box {
.info {
line-height: 1.5;
border: 1px solid #444444;
border-radius: 10px;
padding: 10px;
margin: 10px;
background-color: #1c1c1c;
strong {
color: #f56c6c;
}
}
.invite-qrcode {
text-align: center;
margin: 15px;
}
.invite-url {
margin: 10px;
padding: 8px;
display: flex;
justify-content: space-between;
border: 1px solid #444444;
border-radius: 10px;
background-color: #1c1c1c;
span {
position: relative;
font-size: 14px;
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
top: 0px;
}
}
}
.invite-stats {
padding: 10px 10px;
.item-box {
border-radius: 10px;
padding: 10 0px;
.el-col {
height: 80px;
display: flex;
align-items: center;
justify-content: center;
.iconfont {
font-size: 30px;
}
.item-info {
font-size: 14px;
.text, .num {
padding: 3px 0;
text-align: center;
}
.num {
font-size: 14px;
}
}
}
}
.yellow {
background-color: #ffeecc;
color: #D68F00;
}
.blue {
background-color: #D6E4FF;
color: #1062FE;
}
.green {
background-color: #E7F8EB;
color: #2D9F46;
}
}
}
}

View File

@ -0,0 +1,94 @@
.page-invitation {
display flex
justify-content center
background-color #1c1c1c
height 100vh
overflow-x hidden
overflow-y visible
.inner {
max-width 100%
width 100%
color #fff
.center {
display flex
justify-content center
align-items center
}
.title {
color #fff
text-align center
font-size 16px
font-weight bold
}
.share-box {
.info {
line-height 1.5
border 1px solid #444444
border-radius 10px
padding 10px
margin 10px
background-color #1c1c1c
strong {
color #f56c6c
}
}
.invite-qrcode {
text-align center
margin 15px
}
.invite-url {
margin 10px
padding 8px
display flex
justify-content space-between
border 1px solid #444444
border-radius 10px
background-color #1c1c1c
span {
position relative
font-size 14px
font-family 'Microsoft YaHei', '', Arial, sans-serif
top 0px
}
}
}
.invite-stats {
padding 10px 10px
.item-box {
border-radius 10px
padding 10 0px
.el-col {
height 80px
display flex
align-items center
justify-content center
.iconfont {
font-size 30px
}
.item-info {
font-size 14px
.text, .num {
padding 3px 0
text-align center
}
.num {
font-size 14px
}
}
}
}
.yellow {
background-color #ffeecc
color #D68F00
}
.blue {
background-color #D6E4FF
color #1062FE
}
.green {
background-color #E7F8EB
color #2D9F46
}
}
}
}

View File

@ -0,0 +1,156 @@
.app-background {
background-color: #1c1c1c;
height: 100vh;
}
.member {
background-color: #282828;
height: 100vh;
.el-dialog {
.el-dialog__body {
padding-top: 5px;
.pay-container {
.count-down {
display: flex;
justify-content: center;
}
.pay-qrcode {
display flex
justify-content center
.el-image {
width: 25px;
height:25px;
}
}
.tip {
display flex
justify-content center
.el-icon {
font-size: 20px;
}
.text {
font-size: 16px;
margin-left: 10px;
}
}
.tip.success {
color: #07c160;
}
}
}
}
.title {
text-align: center;
background-color: #282828;
font-size:16px;
color: #ffffff;
padding: 10px;
}
.inner {
color: #ffffff;
overflow-x hidden
overflow-y visible
.user-profile {
padding: 0 20px 0 20px;
background-color: #282828;
color: #ffffff;
border-radius:10px;
width: 400px;
height: 100vh;
.el-form-item__label {
color: #ffffff;
justify-content: start;
}
.user-opt {
.el-col {
padding: 10px;
.el-button {
width: 100%;
}
}
}
}
.product-box {
.info {
.el-alert__description {
font-size: 14px !important;
margin: 0;
}
padding: 10px 20px 20px 0;
}
.list-box {
.product-item {
border: 1px solid #1c1c1c;
border-radius: 6px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease; /* 添加过渡效果 */
.image-container {
display flex
justify-content center
.el-image {
padding: 6px;
.el-image__inner {
border-radius: 10px;
}
}
}
.product-title {
display: flex;
padding: 10px;
.name {
width: 100%;
text-align: center;
font-size: 16px;
font-weight: bold;
color: #2cc995;
}
}
.product-info {
padding: 10px 20px;
font-size: 14px;
color: #999999;
.info-line {
display: flex;
width: 100%;
padding: 5px 0;
.label {
display: flex;
width: 100%;
}
.price, .expire {
display: flex;
width: 90px;
justify-content: right;
}
.price {
color: #f56c6c;
}
.expire {
color: #409eff;
}
}
.pay-way {
padding: 10px 0;
display: flex;
justify-content: space-between;
.iconfont {
margin-right: 5px;
}
}
}
&:hover {
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */
transform: translateY(-10px); /* 向上移动10像素 */
}
}
}
.headline {
padding: 0 20px 20px 0;
}
.user-order {
padding: 0 20px 20px 0;
}
}
}
}

View File

@ -0,0 +1,149 @@
.app-background {
background-color #1c1c1c
height 100vh
}
.member {
background-color #1c1c1c
height 100vh
.el-dialog {
.el-dialog__body {
padding-top 5px
.pay-container {
.count-down {
display flex
justify-content center
}
.pay-qrcode {
display flex
justify-content center
.el-image {
width 280px
height 280px
}
}
.tip {
display flex
justify-content center
.el-icon {
font-size 20px
}
.text {
font-size 16px
margin-left 10px
}
}
.tip.success {
color #07c160
}
}
}
}
.title {
text-align center
background-color #1c1c1c
font-size 16px
color #ffffff
padding 5px
font-weight bold
}
.inner {
color #ffffff
overflow-x hidden
overflow-y visible
.user-profile {
padding 0 20px 0 20px
background-color #1c1c1c
color #ffffff
border-radius 10px
width 100%
height 91vh
.el-form-item__label {
color #ffffff
justify-content start
}
.user-opt {
.el-col {
padding 8px
.el-button {
width 100%
}
}
}
}
.product-box {
.info {
.el-alert__description {
font-size 14px !important
color #1c1c1c
margin 0
}
padding 0 20px 0 20px
}
.list-box {
padding 0 20px 0 5px
.product-item {
border 1px solid #5f5f5f
border-radius 6px
overflow hidden
cursor pointer
transition all 0.3s ease /* */
.image-container {
display flex
justify-content center
.el-image {
width 50px
.el-image__inner {
border-radius 10px
}
}
}
.product-title {
display flex
padding 5px
.name {
width 100%
text-align center
font-size 16px
font-weight bold
color #2cc995
}
}
.product-info {
padding 10px 20px
font-size 14px
color #999999
.info-line {
display flex
width 100%
padding 2px 0
.label {
display flex
width 100%
}
.price, .expire {
display flex
width 100%
justify-content right
}
.price {
color #f56c6c
}
.expire {
color #409eff
}
}
.pay-way {
padding 5px 0
display flex
justify-content space-between
}
}
& hover {
box-shadow 0 0 10px rgba(71, 255, 241, 0.6) /* */
transform translateY(-10px) /* 10 */
}
}
}
}
}
}

View File

@ -148,7 +148,6 @@
overflow hidden overflow hidden
border-radius 6px border-radius 6px
transition: all 0.3s ease; /* */ transition: all 0.3s ease; /* */
position relative
.opt { .opt {
.opt-line { .opt-line {
@ -184,19 +183,6 @@
} }
} }
} }
.remove {
display none
position absolute
right 10px
top 10px
}
&:hover{
.remove {
display block
}
}
} }
.animate { .animate {

View File

@ -0,0 +1,115 @@
.task-list-box {
width: 100%;
padding: 10px;
color: #fff;
overflow-x: hidden;
}
.task-list-box .running-job-list .job-item {
width: 100%;
padding: 2px;
background-color: #555;
}
.task-list-box .running-job-list .job-item .job-item-inner {
position: relative;
height: 100%;
overflow: hidden;
}
.task-list-box .running-job-list .job-item .job-item-inner .progress {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.task-list-box .running-job-list .job-item .job-item-inner .progress span {
font-size: 20px;
color: #fff;
}
.task-list-box .finish-job-list .job-item {
width: 100%;
height: 100%;
}
.task-list-box .finish-job-list .job-item .opt .opt-line {
margin: 6px 0;
}
.task-list-box .finish-job-list .job-item .opt .opt-line ul {
display: flex;
flex-flow: row;
}
.task-list-box .finish-job-list .job-item .opt .opt-line ul li {
margin-right: 10px;
}
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
padding: 3px 0;
width: 44px;
text-align: center;
border-radius: 5px;
display: block;
cursor: pointer;
background-color: #4e5058;
color: #fff;
}
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
background-color: #6d6f78;
}
.task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
font-size: 20px;
cursor: pointer;
}
.task-list-box .el-image {
width: 100%;
height: 100%;
max-height: 240px;
}
.task-list-box .el-image img {
height: 240px;
}
.task-list-box .el-image .el-image-viewer__wrapper img {
width: auto;
height: auto;
}
.task-list-box .el-image .image-slot {
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
height: 100%;
min-height: 200px;
color: #fff;
}
.task-list-box .el-image .image-slot .iconfont {
font-size: 50px;
margin-bottom: 10px;
}
.task-list-box .el-image.upscale {
max-height: 312px;
}
.task-list-box .el-image.upscale img {
height: 312px;
}
.task-list-box .el-image.upscale .el-image-viewer__wrapper img {
width: auto;
height: auto;
}

View File

@ -0,0 +1,142 @@
.task-list-box {
width 100%
padding 5px
color #ffffff
overflow-x hidden
.running-job-list {
.job-item {
//border: 1px solid #454545;
width: 100%;
padding 2px
background-color #555555
.job-item-inner {
position relative
height 100%
overflow hidden
.progress {
position absolute
width 100%
height 100%
top 0
left 0
display flex
justify-content center
align-items center
span {
font-size 20px
color #ffffff
}
}
}
}
}
.finish-job-list {
.job-item {
width 100%
height 100%
border 1px solid #666666
padding 6px
overflow hidden
border-radius 6px
transition: all 0.3s ease; /* */
.opt {
.opt-line {
margin 6px 0
ul {
display flex
flex-flow row
li {
margin-right 6px
a {
padding 3px 0
width 40px
text-align center
border-radius 5px
display block
cursor pointer
background-color #4E5058
color #ffffff
&:hover {
background-color #6D6F78
}
}
}
.show-prompt {
font-size 20px
cursor pointer
}
}
}
}
}
.animate {
&:hover {
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* */
transform: translateY(-10px); /* 10 */
}
}
}
.el-image {
width 100%
height 100%
overflow visible
img {
height 240px
}
.el-image-viewer__wrapper {
img {
width auto
height auto
}
}
.image-slot {
display flex
flex-flow column
justify-content center
align-items center
height 100%
min-height 200px
color #ffffff
height 240px
.iconfont {
font-size 50px
margin-bottom 10px
}
}
}
.el-image.upscale {
max-height 310px
img {
height 310px
}
.el-image-viewer__wrapper {
img {
width auto
height auto
}
}
}
}

View File

@ -23,7 +23,7 @@ const routes = [
{ {
name: 'image-sd', name: 'image-sd',
path: '/sd/', path: '/sd/',
meta: {title: 'Stable Diffusion 绘画中心'}, meta: {title: 'stable diffusion 绘画中心'},
component: () => import('@/views/ImageSd.vue'), component: () => import('@/views/ImageSd.vue'),
}, },
{ {
@ -80,7 +80,7 @@ const routes = [
{ {
path: '/admin/login', path: '/admin/login',
name: 'admin-login', name: 'admin-login',
meta: {title: 'Chat-Plus 控制台登录'}, meta: {title: 'ChatPuls 控制台登录'},
component: () => import('@/views/admin/Login.vue'), component: () => import('@/views/admin/Login.vue'),
}, },
{ {
@ -88,7 +88,7 @@ const routes = [
path: '/admin', path: '/admin',
redirect: '/admin/dashboard', redirect: '/admin/dashboard',
component: () => import("@/views/admin/Home.vue"), component: () => import("@/views/admin/Home.vue"),
meta: {title: 'ChatGPT-Plus 管理后台'}, meta: {title: 'ChatPuls 管理后台'},
children: [ children: [
{ {
path: '/admin/dashboard', path: '/admin/dashboard',
@ -151,10 +151,28 @@ const routes = [
component: () => import('@/views/admin/LoginLog.vue'), component: () => import('@/views/admin/LoginLog.vue'),
}, },
{ {
path: '/admin/functions', path: '/admin/demo/form',
name: 'admin-functions', name: 'admin-form',
meta: {title: '函数管理'}, meta: {title: '表单页面'},
component: () => import('@/views/admin/Functions.vue'), component: () => import('@/views/admin/demo/Form.vue'),
},
{
path: '/admin/demo/table',
name: 'admin-table',
meta: {title: '数据列表'},
component: () => import('@/views/admin/demo/Table.vue'),
},
{
path: '/admin/demo/import',
name: 'admin-import',
meta: {title: '导入数据'},
component: () => import('@/views/admin/demo/Import.vue'),
},
{
path: '/admin/demo/editor',
name: 'admin-editor',
meta: {title: '富文本编辑器'},
component: () => import('@/views/admin/demo/Editor.vue'),
}, },
] ]
}, },
@ -167,7 +185,7 @@ const routes = [
{ {
name: 'mobile', name: 'mobile',
path: '/mobile', path: '/mobile',
meta: {title: process.env.VUE_APP_TITLE}, meta: {title: 'ChatPuls-智能助手V3'},
component: () => import('@/views/mobile/Home.vue'), component: () => import('@/views/mobile/Home.vue'),
redirect: '/mobile/chat/list', redirect: '/mobile/chat/list',
children: [ children: [
@ -177,15 +195,25 @@ const routes = [
component: () => import('@/views/mobile/ChatList.vue'), component: () => import('@/views/mobile/ChatList.vue'),
}, },
{ {
path: '/mobile/setting', path: '/mobile/imageSd',
name: 'mobile-setting', name: 'mobile-imageSd',
component: () => import('@/views/mobile/Setting.vue'), component: () => import('@/views/mobile/ImageSd.vue'),
},
{
path: '/mobile/apps',
name: 'mobile-apps',
component: () => import('@/views/mobile/Apps.vue'),
}, },
{ {
path: '/mobile/profile', path: '/mobile/profile',
name: 'mobile-profile', name: 'mobile-profile',
component: () => import('@/views/mobile/Profile.vue'), component: () => import('@/views/mobile/Profile.vue'),
}, },
{
path: '/mobile/invitation',
name: 'mobile-invitation',
component: () => import('@/views/mobile/Invitation.vue'),
},
] ]
}, },
{ {

View File

@ -0,0 +1,15 @@
<template>
<div class="app-background">
<div class="mobile-setting container">
<div class="title" style="padding-top: 50px">应用广场</div>
<div class="title" style="padding-top: 200px">视频生成器即将开启敬请期待</div>
</div>
</div>
</template>
<script setup>
</script>
<style lang="stylus">
@import "@/assets/css/mobile-apps.styl"
</style>

View File

@ -1,9 +1,12 @@
<template> <template>
<div class="app-background">
<div v-if="isLogin" class="container mobile-chat-list"> <div v-if="isLogin" class="container mobile-chat-list">
<van-nav-bar <van-nav-bar
:title="title" :title="title"
left-text="新建会话" left-text="新建会话"
@click-left="showPicker = true"> @click-left="showPicker = true"
custom-class="navbar"
>
<template #right> <template #right>
<van-icon name="delete-o" @click="clearAllChatHistory"/> <van-icon name="delete-o" @click="clearAllChatHistory"/>
</template> </template>
@ -14,6 +17,7 @@
v-model="chatName" v-model="chatName"
input-align="center" input-align="center"
placeholder="请输入会话标题" placeholder="请输入会话标题"
custom-class="van-search"
@input="search" @input="search"
/> />
@ -42,8 +46,9 @@
</van-swipe-cell> </van-swipe-cell>
</van-list> </van-list>
</div> </div>
</div>
<van-popup v-model:show="showPicker" position="bottom"> <van-popup v-model:show="showPicker" position="bottom" class="popup">
<van-picker <van-picker
:columns="columns" :columns="columns"
title="选择模型和角色" title="选择模型和角色"
@ -64,13 +69,13 @@
</van-picker> </van-picker>
</van-popup> </van-popup>
<van-dialog v-model:show="showEditChat" title="修改对话标题" show-cancel-button @confirm="saveTitle"> <van-dialog v-model:show="showEditChat" title="修改对话标题" show-cancel-button class="dialog" @confirm="saveTitle">
<van-field v-model="tmpChatTitle" label="" placeholder="请输入对话标题"/> <van-field v-model="tmpChatTitle" label="" placeholder="请输入对话标题" class="field"/>
</van-dialog> </van-dialog>
<bind-mobile v-if="isLogin" :show="showBindMobileDialog" :mobile="loginUser.mobile" <bind-mobile v-if="isLogin" :show="showBindMobileDialog" :mobile="loginUser.mobile"
@hide="showBindMobileDialog = false"/> @hide="showBindMobileDialog = false"/>
</div> </div>
</template> </template>
<script setup> <script setup>
@ -260,49 +265,5 @@ const removeChat = (item) => {
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
$fontSize = 16px; @import "@/assets/css/mobile-chat-list.styl"
.mobile-chat-list {
.content {
.van-cell__value {
.chat-list-item {
display flex
font-size $fontSize
.van-image {
min-width 32px
width 32px
height 32px
}
.van-ellipsis {
margin-top 5px;
margin-left 10px;
}
}
}
}
.van-picker-column {
.picker-option {
display flex
width 100%
padding 0 10px
.van-image {
width 20px;
height 20px;
margin-right 5px
}
}
}
.van-nav-bar {
.van-nav-bar__right {
.van-icon {
font-size 20px;
}
}
}
}
</style> </style>

View File

@ -1,5 +1,6 @@
<template> <template>
<van-config-provider :theme="getMobileTheme()"> <div class="app-background">
<van-config-provider theme="dark">
<div class="mobile-chat" v-loading="loading" element-loading-text="正在连接会话..."> <div class="mobile-chat" v-loading="loading" element-loading-text="正在连接会话...">
<van-sticky ref="navBarRef" :offset-top="0" position="top"> <van-sticky ref="navBarRef" :offset-top="0" position="top">
<van-nav-bar left-arrow left-text="返回" @click-left="router.back()"> <van-nav-bar left-arrow left-text="返回" @click-left="router.back()">
@ -15,6 +16,7 @@
<template #right> <template #right>
<van-icon name="share-o" @click="showShare = true"/> <van-icon name="share-o" @click="showShare = true"/>
</template> </template>
</van-nav-bar> </van-nav-bar>
</van-sticky> </van-sticky>
@ -25,7 +27,8 @@
@select="shareChat" @select="shareChat"
/> />
<div id="message-list-box" :style="{height: winHeight+'px'}" class="message-list-box"> <div class="chat-list-wrapper">
<div id="message-list-box" :style="{height: winHeight + 'px'}" class="message-list-box">
<van-list <van-list
v-model:error="error" v-model:error="error"
:finished="finished" :finished="finished"
@ -57,10 +60,11 @@
</van-cell> </van-cell>
</van-list> </van-list>
</div> </div>
</div>
<div class="chat-box-wrapper">
<van-sticky ref="bottomBarRef" :offset-bottom="0" position="bottom"> <van-sticky ref="bottomBarRef" :offset-bottom="0" position="bottom">
<div class="chat-box">
<van-cell-group> <van-cell-group inset>
<van-field <van-field
v-model="prompt" v-model="prompt"
center center
@ -78,11 +82,12 @@
</template> </template>
</van-field> </van-field>
</van-cell-group> </van-cell-group>
</div>
</van-sticky> </van-sticky>
</div> </div>
</div>
</van-config-provider> </van-config-provider>
</div>
</template> </template>
<script setup> <script setup>
@ -101,6 +106,11 @@ import {checkSession} from "@/action/session";
import {getMobileTheme} from "@/store/system"; import {getMobileTheme} from "@/store/system";
import ChatMidJourney from "@/components/mobile/ChatMidJourney.vue"; import ChatMidJourney from "@/components/mobile/ChatMidJourney.vue";
import QRCode from "qrcode";
import {ElMessage} from "element-plus";
import Clipboard from "clipboard";
import InviteList from "@/components/InviteList.vue";
const winHeight = ref(0) const winHeight = ref(0)
const navBarRef = ref(null) const navBarRef = ref(null)
const bottomBarRef = ref(null) const bottomBarRef = ref(null)
@ -114,6 +124,16 @@ const title = chatConfig.title
const chatId = chatConfig.chatId const chatId = chatConfig.chatId
const loginUser = ref(null) const loginUser = ref(null)
const listBoxHeight = window.innerHeight
const inviteURL = ref("")
const qrImg = ref("")
const inviteChatCalls = ref(0)
const inviteImgCalls = ref(0)
const hits = ref(0)
const regNum = ref(0)
const rate = ref(0)
const isLogin = ref(false)
onMounted(() => { onMounted(() => {
winHeight.value = document.body.offsetHeight - navBarRef.value.$el.offsetHeight - bottomBarRef.value.$el.offsetHeight winHeight.value = document.body.offsetHeight - navBarRef.value.$el.offsetHeight - bottomBarRef.value.$el.offsetHeight
}) })
@ -385,59 +405,10 @@ const shareOptions = [
] ]
const shareChat = () => { const shareChat = () => {
showShare.value = false showShare.value = false
showToast('功能待开发') router.push('/mobile/Invitation');
} }
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
.mobile-chat { @import "@/assets/css/mobile-chat-session.css"
.message-list-box {
padding-top 50px
padding-bottom 10px
overflow-x auto
background #F5F5F5;
.van-cell {
background none
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
}
}
.chat-box {
.icon-box {
.van-icon {
font-size 24px
margin-left 10px;
}
}
}
}
.van-theme-dark {
.mobile-chat {
.message-list-box {
background #232425;
}
}
}
</style>
<style lang="stylus">
.mobile-chat {
.van-nav-bar__title {
.van-dropdown-menu__title {
margin-right 10px
}
.van-cell__title {
text-align left
}
}
.van-nav-bar__right {
.van-icon {
font-size 20px
}
}
}
</style> </style>

View File

@ -1,17 +1,26 @@
<template> <template>
<van-config-provider :theme="getMobileTheme()"> <div class="banner">
<h1 class="banner-title">ChatPuls-V3 智能助理</h1>
</div>
<van-config-provider theme="dark">
<div class="mobile-home"> <div class="mobile-home">
<router-view/> <router-view/>
<van-tabbar
<van-tabbar route v-model="active" @change="onChange"> fixed
<van-tabbar-item to="/mobile/chat/list" name="home" icon="chat-o"></van-tabbar-item> route
<van-tabbar-item to="/mobile/setting" name="setting" icon="setting-o"></van-tabbar-item> v-model="active"
<van-tabbar-item to="/mobile/profile" name="profile" icon="user-o"></van-tabbar-item> active-color="#F5F5F5"
inactive-color="#2CC995"
class="my-tabbar"
>
<van-tabbar-item to="/mobile/chat/list" name="home" icon="chat-o">对话</van-tabbar-item>
<van-tabbar-item to="/mobile/imageSd" name="imageSd" icon="photo-o">绘图</van-tabbar-item>
<van-tabbar-item to="/mobile/apps" name="apps" icon="apps-o">应用</van-tabbar-item>
<van-tabbar-item to="/mobile/profile" name="profile" icon="user-o">我的</van-tabbar-item>
<van-tabbar-item to="/mobile/invitation" name="invitation" icon="share-o">分享</van-tabbar-item>
</van-tabbar> </van-tabbar>
</div> </div>
</van-config-provider> </van-config-provider>
</template> </template>
<script setup> <script setup>
@ -38,33 +47,6 @@ const onChange = (index) => {
</script> </script>
<style lang="stylus"> <style lang="stylus" scoped>
.mobile-home { @import "@/assets/css/mobile-home.css"
.container {
.van-nav-bar {
position fixed
width 100%
}
.content {
padding 46px 10px 0 10px;
}
}
}
//
.van-theme-dark body {
background #1c1c1e
}
.van-toast--fail {
background #fef0f0
color #f56c6c
}
.van-nav-bar {
position fixed
width 100%
}
</style> </style>

View File

@ -0,0 +1,657 @@
<template>
<div class="page-sd" theme="dark">
<div class="inner">
<div class="sd-box" style="padding-top: 35px">
<h2>Ai绘图</h2>
<div class="sd-params" :style="{ height: mjBoxHeight + 'px' }">
<el-form :model="params" label-width="80px" label-position="left">
<div class="param-line" style="padding-top: 10px">
<el-form-item label="采样方法">
<template #default>
<div class="form-item-inner">
<el-select v-model="params.sampler" size="small">
<el-option v-for="item in samplers" :label="item" :value="item" :key="item"/>
</el-select>
<el-tooltip
effect="light"
content="出图效果比较好的一般是 Euler 和 DPM 系列算法"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="图片尺寸">
<template #default>
<div class="form-item-inner">
<el-row :gutter="20">
<el-col :span="12">
<el-input v-model.number="params.width" size="small" placeholder="图片宽度"/>
</el-col>
<el-col :span="12">
<el-input v-model.number="params.height" size="small" placeholder="图片高度"/>
</el-col>
</el-row>
</div>
</template>
</el-form-item>
</div>
<!-- 这是一个示例注释
<div class="param-line">
<el-form-item label="迭代步数">
<template #default>
<div class="form-item-inner">
<el-input v-model.number="params.steps" size="small"/>
<el-tooltip
effect="light"
content="值越大则代表细节越多,同时也意味着出图速度越慢"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="引导系数">
<template #default>
<div class="form-item-inner">
<el-input v-model.number="params.cfg_scale" size="small"/>
<el-tooltip
effect="light"
content="提示词引导系数,图像在多大程度上服从提示词<br/> 较低值会产生更有创意的结果"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="随机因子">
<template #default>
<div class="form-item-inner">
<el-input v-model.number="params.seed" size="small"/>
<el-tooltip
effect="light"
content="随机数种子,相同的种子会得到相同的结果<br/> 设置为 -1 则每次随机生成种子"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
<el-tooltip
effect="light"
content="使用随机数"
raw-content
placement="right"
>
<el-icon @click="params.seed = -1">
<Orange/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
-->
<!-- 这是一个示例注释
<div class="param-line">
<el-form-item label="面部修复">
<template #default>
<div class="form-item-inner">
<el-switch v-model="params.face_fix" style="--el-switch-on-color: #47fff1;"/>
<el-tooltip
effect="light"
content="仅对绘制人物图像有效果。"
raw-content
placement="right"
>
<el-icon style="margin-top: 6px">
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="高清修复">
<template #default>
<div class="form-item-inner">
<el-switch v-model="params.hd_fix" style="--el-switch-on-color: #47fff1;"/>
<el-tooltip
effect="light"
content="先以较小的分辨率生成图像,接着方法图像<br />然后在不更改构图的情况下再修改细节"
raw-content
placement="right"
>
<el-icon style="margin-top: 6px">
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div v-show="params.hd_fix">
<div class="param-line">
<el-form-item label="重绘幅度">
<template #default>
<div class="form-item-inner">
<el-slider v-model.number="params.hd_redraw_rate" :max="1" :step="0.1"
style="width: 180px;--el-slider-main-bg-color:#47fff1"/>
<el-tooltip
effect="light"
content="决定算法对图像内容的影响程度<br />较大的值将得到越有创意的图像"
raw-content
placement="right"
>
<el-icon style="margin-top: 6px">
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="放大算法">
<template #default>
<div class="form-item-inner">
<el-select v-model="params.hd_scale_alg" size="small">
<el-option v-for="item in scaleAlg" :label="item" :value="item" :key="item"/>
</el-select>
<el-tooltip
effect="light"
content="高清修复放大算法主流算法有Latent和ESRGAN_4x"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="放大倍数">
<template #default>
<div class="form-item-inner">
<el-input v-model.number="params.hd_scale" size="small"/>
<el-tooltip
effect="light"
content="随机数种子,相同的种子会得到相同的结果<br/> 设置为 -1 则每次随机生成种子"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
<div class="param-line">
<el-form-item label="迭代步数">
<template #default>
<div class="form-item-inner">
<el-input v-model.number="params.hd_steps" size="small"/>
<el-tooltip
effect="light"
content="重绘迭代步数如果设置为0则设置跟原图相同的迭代步数"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
</template>
</el-form-item>
</div>
</div>
-->
<div class="param-line" v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.8)">
<el-input
v-model="params.prompt"
:autosize="{ minRows: 4, maxRows: 6 }"
type="textarea"
ref="promptRef"
placeholder="正向提示词,例如:“一个中国女孩,黑色的长发,穿着职业女装走在深圳夜晚的街头,看着街上的车流”,输入后点击翻译或翻译并重写按钮后点击生成。"
/>
</div>
<div style="padding: 10px">
<el-button type="primary" @click="translatePrompt" size="small">
<el-icon style="margin-right: 6px;font-size: 18px;">
<Refresh/>
</el-icon>
翻译
</el-button>
<el-tooltip
class="box-item"
effect="dark"
raw-content
content="使用 AI 翻译并重写提示词,<br/>增加更多细节,风格等描述"
placement="top-end"
>
<el-button type="success" @click="rewritePrompt" size="small">
<el-icon style="margin-right: 6px;font-size: 18px;">
<Refresh/>
</el-icon>
翻译并重写
</el-button>
</el-tooltip>
<el-tag type="success" style="margin-left: 12px;">绘图可用额度{{ imgCalls }}</el-tag>
</div>
<!--
<div class="param-line pt">
<span>反向提示词</span>
<el-tooltip
effect="light"
content="不希望出现的元素,下面给了默认的起手式"
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</div>
<div class="param-line">
<el-input
v-model="params.negative_prompt"
:autosize="{ minRows: 4, maxRows: 6 }"
type="textarea"
placeholder="反向提示词"
/>
</div>
-->
</el-form>
</div>
<div class="submit-btn">
<el-button color="#47fff1" :dark="false" round @click="generate">立即生成</el-button>
</div>
<div class="task-list-box">
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
<h2>任务列表</h2>
<div class="running-job-list">
<ItemList :items="runningJobs" v-if="runningJobs.length > 0" width="240">
<template #default="scope">
<div class="job-item">
<div v-if="scope.item.progress > 0" class="job-item-inner">
<el-image :src="scope.item['img_url']"
fit="cover"
loading="lazy">
<template #placeholder>
<div class="image-slot">
正在加载图片
</div>
</template>
<template #error>
<div class="image-slot">
<el-icon v-if="scope.item['img_url'] !== ''">
<Picture/>
</el-icon>
</div>
</template>
</el-image>
<div class="progress">
<el-progress type="circle" :percentage="scope.item.progress" :width="100" color="#47fff1"/>
</div>
</div>
<el-image fit="cover" v-else>
<template #error>
<div class="image-slot">
<i class="iconfont icon-quick-start"></i>
<span>任务正在排队中</span>
</div>
</template>
</el-image>
</div>
</template>
</ItemList>
<el-empty :image-size="100" v-else/>
</div>
<h2>创作记录</h2>
<div class="finish-job-list">
<ItemList :items="finishedJobs" v-if="finishedJobs.length > 0" width="480" :gap="20">
<template #default="scope">
<div class="job-item animate" @click="showTask(scope.item)">
<el-image
:src="scope.item['img_url']+'?imageView2/1/w/480/h/480/q/75'"
fit="cover"
loading="lazy">
<template #placeholder>
<div class="image-slot">
正在加载图片
</div>
</template>
<template #error>
<div class="image-slot">
<el-icon>
<Picture/>
</el-icon>
</div>
</template>
</el-image>
</div>
</template>
</ItemList>
<el-empty :image-size="100" v-else/>
</div> <!-- end finish job list-->
</div>
</div><!-- end task list box -->
</div>
</div>
<el-dialog v-model="showTaskDialog" title="绘画任务详情" :fullscreen="true">
<el-row class="mobile-form-row">
<el-col :span="24" class="mobile-form-col">
<div class="img-container" :style="{maxHeight: fullImgHeight+'px'}">
<el-image :src="item['img_url']" fit="contain"/>
</div>
<el-col :span="50">
<div class="task-info">
<div class="info-line">
<el-divider>
正向提示词
</el-divider>
<div class="prompt">
<span>{{ item.prompt }}</span>
<el-icon class="copy-prompt" :data-clipboard-text="item.prompt">
<DocumentCopy/>
</el-icon>
</div>
</div>
<div class="info-line">
<el-divider>
反向提示词
</el-divider>
<div class="prompt">
<span>{{ item.params.negative_prompt }}</span>
<el-icon class="copy-prompt" :data-clipboard-text="item.params.negative_prompt">
<DocumentCopy/>
</el-icon>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>采样方法</label>
<div class="item-value">{{ item.params.sampler }}</div>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>图片尺寸</label>
<div class="item-value">{{ item.params.width }} x {{ item.params.height }}</div>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>迭代步数</label>
<div class="item-value">{{ item.params.steps }}</div>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>引导系数</label>
<div class="item-value">{{ item.params.cfg_scale }}</div>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>随机因子</label>
<div class="item-value">{{ item.params.seed }}</div>
</div>
</div>
<div v-if="item.params.hd_fix">
<el-divider>
高清修复
</el-divider>
<div class="info-line">
<div class="wrapper">
<label>重绘幅度</label>
<div class="item-value">{{ item.params.hd_redraw_rate }}</div>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>放大算法</label>
<div class="item-value">{{ item.params.hd_scale_alg }}</div>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>放大倍数</label>
<div class="item-value">{{ item.params.hd_scale }}</div>
</div>
</div>
<div class="info-line">
<div class="wrapper">
<label>迭代步数</label>
<div class="item-value">{{ item.params.hd_steps }}</div>
</div>
</div>
</div>
<div class="copy-params">
<el-button type="primary" round @click="copyParams(item)">画一张同款的</el-button>
</div>
</div>
</el-col>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script setup>
import {onMounted, ref} from "vue"
import {DocumentCopy, InfoFilled, Orange, Picture, Refresh} from "@element-plus/icons-vue";
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage, ElNotification} from "element-plus";
import ItemList from "@/components/ItemList.vue";
import Clipboard from "clipboard";
import {checkSession} from "@/action/session";
import {useRouter} from "vue-router";
import {getSessionId} from "@/store/session";
const listBoxHeight = ref(window.innerHeight - 40)
// const mjBoxHeight = ref(window.innerHeight - 150)
const fullImgHeight = ref(window.innerHeight - 60)
const showTaskDialog = ref(false)
const item = ref({})
const loading = ref(false)
window.onresize = () => {
listBoxHeight.value = window.innerHeight - 40
// mjBoxHeight.value = window.innerHeight - 150
}
const samplers = ["Euler a", "Euler", "DPM2 a Karras", "DPM++ 2S a Karras", "DPM++ 2M Karras", "DPM++ SDE Karras", "DPM2", "DPM2 a", "DPM++ 2S a", "DPM++ 2M", "DPM++ SDE", "DPM fast", "DPM adaptive",
"LMS Karras", "DPM2 Karras", "DDIM", "PLMS", "UniPC", "LMS", "Heun",]
const scaleAlg = ["Latent", "ESRGAN_4x", "R-ESRGAN 4x+", "SwinIR_4x", "LDSR"]
const params = ref({
width: 768,
height: 1024,
sampler: samplers[0],
seed: -1,
steps: 20,
cfg_scale: 7,
face_fix: false,
hd_fix: false,
hd_redraw_rate: 0.7,
hd_scale: 2,
hd_scale_alg: scaleAlg[0],
hd_steps: 10,
prompt: "",
negative_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
})
const runningJobs = ref([])
const finishedJobs = ref([])
const router = useRouter()
//
const _params = router.currentRoute.value.params["copyParams"]
if (_params) {
params.value = JSON.parse(_params)
}
const imgCalls = ref(0)
const rewritePrompt = () => {
loading.value = true
httpPost("/api/prompt/rewrite", {"prompt": params.value.prompt}).then(res => {
params.value.prompt = res.data
loading.value = false
}).catch(e => {
loading.value = false
ElMessage.error("翻译失败:" + e.message)
})
}
const translatePrompt = () => {
loading.value = true
httpPost("/api/prompt/translate", {"prompt": params.value.prompt}).then(res => {
params.value.prompt = res.data
loading.value = false
}).catch(e => {
loading.value = false
ElMessage.error("翻译失败:" + e.message)
})
}
onMounted(() => {
checkSession().then(user => {
imgCalls.value = user['img_calls']
fetchRunningJobs(user.id)
fetchFinishJobs(user.id)
}).catch(() => {
router.push('/login')
});
const fetchRunningJobs = (userId) => {
//
httpGet(`/api/sd/jobs?status=0&user_id=${userId}`).then(res => {
const jobs = res.data
const _jobs = []
for (let i = 0; i < jobs.length; i++) {
if (jobs[i].progress === -1) {
ElNotification({
title: '任务执行失败',
message: "任务ID" + jobs[i]['task_id'],
type: 'error',
})
continue
}
_jobs.push(jobs[i])
}
runningJobs.value = _jobs
setTimeout(() => fetchRunningJobs(userId), 5000)
}).catch(e => {
ElMessage.error("获取任务失败:" + e.message)
})
}
//
const fetchFinishJobs = (userId) => {
httpGet(`/api/sd/jobs?status=1&user_id=${userId}`).then(res => {
finishedJobs.value = res.data
setTimeout(() => fetchFinishJobs(userId), 5000)
}).catch(e => {
ElMessage.error("获取任务失败:" + e.message)
})
}
const clipboard = new Clipboard('.copy-prompt');
clipboard.on('success', () => {
ElMessage.success("复制成功!");
})
clipboard.on('error', () => {
ElMessage.error('复制失败!');
})
})
//
const promptRef = ref(null)
const generate = () => {
if (params.value.prompt === '') {
promptRef.value.focus()
return ElMessage.error("请输入绘画提示词!")
}
if (params.value.seed === '') {
params.value.seed = -1
}
params.value.session_id = getSessionId()
httpPost("/api/sd/image", params.value).then(() => {
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
imgCalls.value -= 1
}).catch(e => {
ElMessage.error("任务推送失败:" + e.message)
})
}
const showTask = (row) => {
item.value = row
showTaskDialog.value = true
}
const copyParams = (row) => {
params.value = row.params
showTaskDialog.value = false
}
</script>
<style lang="stylus">
@import "@/assets/css/image-mobile-sd.css"
</style>

View File

@ -0,0 +1,152 @@
<template>
<div class="page-invitation" :style="{height: listBoxHeight + 'px'}">
<div class="inner">
<div class="title" style="padding-top: 50px">会员推广计划</div>
<div class="share-box">
<div class="info">
感谢您把此应用分享给您身边的朋友分享成功注册后您将获得 <strong>{{ inviteChatCalls }}</strong>
次对话额度以及
<strong>{{ inviteImgCalls }}</strong> 次AI绘画额度作为奖励
你可以保存下面的二维码或者直接复制分享您的专属推广链接发送给微信好友
</div>
<div class="invite-qrcode">
<el-image :src="qrImg" fit="contain" />
</div>
<div class="invite-url">
<span>{{ inviteURL }}</span>
<el-button type="primary" plain class="copy-link" :data-clipboard-text="inviteURL">复制链接</el-button>
</div>
</div>
<div class="invite-stats">
<el-row :gutter="20">
<el-col :span="8">
<div class="item-box yellow">
<el-row :gutter="10">
<el-col :span="10">
<div class="item-icon">
<i class="iconfont icon-role"></i>
</div>
</el-col>
<el-col :span="14">
<div class="item-info">
<div class="num">{{ hits }}</div>
<div class="text">点击量</div>
</div>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="8">
<div class="item-box blue">
<el-row :gutter="10">
<el-col :span="10">
<div class="item-icon">
<i class="iconfont icon-order"></i>
</div>
</el-col>
<el-col :span="14">
<div class="item-info">
<div class="num">{{ regNum }}</div>
<div class="text">注册量</div>
</div>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="8">
<div class="item-box green">
<el-row :gutter="10">
<el-col :span="10">
<div class="item-icon">
<i class="iconfont icon-chart"></i>
</div>
</el-col>
<el-col :span="14">
<div class="item-info">
<div class="num">{{ rate }}%</div>
<div class="text">转化率</div>
</div>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<div class="title" style="padding: 20px">您推荐的用户</div>
<div class="invite-logs" style="padding-bottom: 50px">
<invite-list v-if="isLogin"/>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {onMounted, ref} from "vue"
import QRCode from "qrcode";
import {httpGet} from "@/utils/http";
import {ElMessage} from "element-plus";
import Clipboard from "clipboard";
import InviteList from "@/components/InviteList.vue";
import {checkSession} from "@/action/session";
import {useRouter} from "vue-router";
const listBoxHeight = window.innerHeight
const inviteURL = ref("")
const qrImg = ref("")
const inviteChatCalls = ref(0)
const inviteImgCalls = ref(0)
const hits = ref(0)
const regNum = ref(0)
const rate = ref(0)
const router = useRouter()
const isLogin = ref(false)
onMounted(() => {
checkSession().then(() => {
isLogin.value = true
httpGet("/api/invite/code").then(res => {
const text = `${location.protocol}//${location.host}/register?invite_code=${res.data.code}`
hits.value = res.data["hits"]
regNum.value = res.data["reg_num"]
if (hits.value > 0) {
rate.value = ((regNum.value / hits.value) * 100).toFixed(2)
}
QRCode.toDataURL(text, {width: 300, height: 300, margin: 2}, (error, url) => {
if (error) {
console.error(error)
} else {
qrImg.value = url;
}
});
inviteURL.value = text
}).catch(e => {
ElMessage.error("获取邀请码失败:" + e.message)
})
httpGet("/api/admin/config/get?key=system").then(res => {
inviteChatCalls.value = res.data["invite_chat_calls"]
inviteImgCalls.value = res.data["invite_img_calls"]
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
}).catch(() => {
router.push('/login')
});
//
const clipboard = new Clipboard('.copy-link');
clipboard.on('success', () => {
ElMessage.success('复制成功!');
})
clipboard.on('error', () => {
ElMessage.error('复制失败!');
})
})
</script>
<style lang="stylus" scoped>
@import "@/assets/css/mobile-invitation.styl"
</style>

View File

@ -1,128 +1,294 @@
<template> <template>
<div class="mobile-user-profile container"> <div class="app-background" theme="dark">
<van-nav-bar :title="title"/> <div class="member">
<div class="title" style="padding-top: 50px">
<div class="content"> 会员中心
<van-form @submit="save">
<van-cell-group inset v-model="form">
<van-field
v-model="form.mobile"
name="手机号"
label="手机号"
readonly
disabled
placeholder="手机号"
/>
<van-field label="头像">
<template #input>
<van-uploader v-model="fileList"
reupload max-count="1"
:deletable="false"
:after-read="afterRead"/>
</template>
</van-field>
<van-field label="剩余对话次数">
<template #input>
<van-tag type="primary">{{ form.calls }}</van-tag>
</template>
</van-field>
<van-field label="剩余绘图次数">
<template #input>
<van-tag type="primary">{{ form.img_calls }}</van-tag>
</template>
</van-field>
<van-field label="累计消耗tokens">
<template #input>
<van-tag type="primary">{{ form.total_tokens }}</van-tag>
</template>
</van-field>
</van-cell-group>
<div style="margin: 16px;">
<van-button round block type="primary" native-type="submit">
提交
</van-button>
</div> </div>
</van-form> <div class="inner" :style="{height: listBoxHeight + 'px'}">
<div class="product-box">
<ItemList :items="list" v-if="list.length > 0" :gap="30" >
<template #default="scope">
<div class="product-item" :style="{width: scope.width+'px'}">
<div class="image-container">
<el-image :src="vipImg" fit="cover"/>
</div>
<div class="product-title">
<span class="name">{{ scope.item.name }}</span>
</div>
<div class="product-info">
<div class="info-line">
<span class="label">商品原价</span>
<span class="price">{{ scope.item.price }}</span>
</div>
<div class="info-line">
<span class="label">促销立减</span>
<span class="price">{{ scope.item.discount }}</span>
</div>
<div class="info-line">
<span class="label">有效期</span>
<span class="expire" v-if="scope.item.days > 0">{{ scope.item.days }}</span>
<span class="expire" v-else>当月有效</span>
</div>
<div class="pay-way">
<el-button type="primary" @click="alipay(scope.item)" size="small" v-if="payWays['alipay']">
<i class="iconfont icon-alipay"></i> 支付宝
</el-button>
<el-button type="success" @click="huPiPay(scope.item)" size="small" v-if="payWays['hupi']">
<span v-if="payWays['hupi']['name'] === 'wechat'"><i class="iconfont icon-wechat-pay"></i> 微信</span>
<span v-else><i class="iconfont icon-alipay"></i> 支付宝</span>
</el-button>
</div> </div>
</div> </div>
</div>
</template>
</ItemList>
</div>
<el-row>
<div class="user-profile">
<user-profile/>
<el-row class="user-opt" :gutter="20">
<el-col :span="12">
<el-button type="primary" @click="showPasswordDialog = true">修改密码</el-button>
</el-col>
<el-col :span="12">
<el-button type="primary" @click="showBindMobileDialog = true">绑定手机号</el-button>
</el-col>
<el-col :span="12">
<el-button type="primary" v-if="enableReward" @click="showRewardDialog = true">加入众筹</el-button>
</el-col>
<el-col :span="12">
<el-button type="primary" v-if="enableReward" @click="showRewardVerifyDialog = true">众筹核销
</el-button>
</el-col>
<el-col :span="24" style="padding-bottom: 50px;">
<el-button type="danger" round @click="logout">退出登录</el-button>
</el-col>
</el-row>
</div>
</el-row>
</div>
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false"/>
<password-dialog v-if="isLogin" :show="showPasswordDialog" width="100%" @hide="showPasswordDialog = false"
@logout="logout"/>
<bind-mobile v-if="isLogin" :show="showBindMobileDialog" width="100%" :mobile="user.mobile"
@hide="showBindMobileDialog = false"/>
<reward-verify v-if="isLogin" :show="showRewardVerifyDialog" @hide="showRewardVerifyDialog = false"/>
<el-dialog
v-model="showRewardDialog"
:show-close="true"
width="100%"
title="参与众筹"
>
<el-alert type="info" :closable="false">
<div style="font-size: 14px">您好众筹 9.9就可以兑换 100 次对话以此来覆盖我们的 OpenAI
账单和服务器的费用<strong
style="color: #f56c6c">由于本人没有开通微信支付付款后请凭借转账单号,点击众筹核销按钮手动核销</strong>
</div>
</el-alert>
<div style="text-align: center;padding-top: 10px;">
<el-image v-if="enableReward" :src="rewardImg"/>
</div>
</el-dialog>
<el-dialog
v-model="showPayDialog"
:close-on-click-modal="false"
:show-close="true"
:width="400"
title="充值订单支付">
<div class="pay-container">
<div class="count-down">
<count-down :second="orderTimeout" @timeout="refreshPayCode" ref="countDownRef"/>
</div>
<div class="pay-qrcode" v-loading="loading">
<el-image :src="qrcode"/>
</div>
<div class="tip success" v-if="text !== ''">
<el-icon>
<SuccessFilled/>
</el-icon>
<span class="text">{{ text }}</span>
</div>
<div class="tip" v-else>
<el-icon>
<InfoFilled/>
</el-icon>
<span class="text">请打开手机{{ payName }}扫码支付</span>
</div>
</div>
</el-dialog>
</div>
</div>
</template> </template>
<script setup> <script setup>
import {onMounted, ref} from "vue"; import {onMounted, ref} from "vue"
import {showFailToast, showNotify, showSuccessToast} from "vant"; import {ElMessage} from "element-plus";
import {httpGet, httpPost} from "@/utils/http"; import {httpGet, httpPost} from "@/utils/http";
import Compressor from 'compressorjs'; import ItemList from "@/components/ItemList.vue";
import {InfoFilled, SuccessFilled} from "@element-plus/icons-vue";
import LoginDialog from "@/components/LoginDialog.vue";
import {checkSession} from "@/action/session";
import UserProfile from "@/components/UserProfile.vue";
import PasswordDialog from "@/components/PasswordDialog.vue";
import BindMobile from "@/components/BindMobile.vue";
import RewardVerify from "@/components/RewardVerify.vue";
import {useRouter} from "vue-router";
import {removeUserToken} from "@/store/session";
import UserOrder from "@/components/UserOrder.vue";
import CountDown from "@/components/CountDown.vue";
const listBoxHeight = window.innerHeight - 97
const list = ref([])
const showLoginDialog = ref(false)
const showPayDialog = ref(false)
const vipImg = ref("/images/vip.png")
const enableReward = ref(false) //
const rewardImg = ref('/images/reward.png')
const qrcode = ref("")
const showPasswordDialog = ref(false);
const showBindMobileDialog = ref(false);
const showRewardDialog = ref(false);
const showRewardVerifyDialog = ref(false);
const text = ref("")
const user = ref(null)
const isLogin = ref(false)
const router = useRouter()
const curPayProduct = ref(null)
const activeOrderNo = ref("")
const countDownRef = ref(null)
const orderTimeout = ref(1800)
const loading = ref(true)
const orderPayInfoText = ref("")
const payWays = ref({})
const payName = ref("支付宝")
const curPay = ref("alipay") //
const title = ref('用户设置')
const form = ref({
username: '',
nickname: '',
mobile: '',
avatar: '',
calls: 0,
tokens: 0
})
const fileList = ref([
{
url: 'https://fastly.jsdelivr.net/npm/@vant/assets/leaf.jpeg',
message: '上传中...',
}
]);
onMounted(() => { onMounted(() => {
httpGet('/api/user/profile').then(res => { checkSession().then(_user => {
form.value = res.data user.value = _user
fileList.value[0].url = form.value.avatar isLogin.value = true
}).catch((e) => { httpGet("/api/product/list").then((res) => {
console.log(e.message) list.value = res.data
showFailToast('获取用户信息失败') }).catch(e => {
}); ElMessage.error("获取产品套餐失败:" + e.message)
})
}).catch(() => {
router.push("/login")
})
httpGet("/api/admin/config/get?key=system").then(res => {
rewardImg.value = res.data['reward_img']
enableReward.value = res.data['enabled_reward']
orderPayInfoText.value = res.data['order_pay_info_text']
if (res.data['order_pay_timeout'] > 0) {
orderTimeout.value = res.data['order_pay_timeout']
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
httpGet("/api/payment/payWays").then(res => {
payWays.value = res.data
}).catch(e => {
ElMessage.error("获取支付方式失败:" + e.message)
})
}) })
const afterRead = (file) => { // refresh payment qrcode
file.status = 'uploading'; const refreshPayCode = () => {
file.message = '上传中...'; if (curPay.value === 'alipay') {
// alipay()
new Compressor(file.file, { } else if (curPay.value === 'hupi') {
quality: 0.6, huPiPay()
success(result) { }
const formData = new FormData(); }
formData.append('file', result, result.name);
//
httpPost('/api/upload', formData).then((res) => {
form.value.avatar = res.data
file.status = 'success'
showNotify({type: 'success', message: '上传成功'})
}).catch((e) => {
console.log(e.message)
showNotify({type: 'danger', message: '上传失败'})
})
},
error(err) {
console.log(err.message);
},
});
};
const save = () => { const genPayQrcode = () => {
httpPost('/api/user/profile/update', form.value).then(() => { loading.value = true
showSuccessToast('保存成功') text.value = ""
}).catch(() => { httpPost("/api/payment/qrcode", {
showFailToast('保存失败') pay_way: curPay.value,
product_id: curPayProduct.value.id,
user_id: user.value.id
}).then(res => {
showPayDialog.value = true
qrcode.value = res.data['image']
activeOrderNo.value = res.data['order_no']
queryOrder(activeOrderNo.value)
loading.value = false
//
if (countDownRef.value) {
countDownRef.value.resetTimer()
}
}).catch(e => {
ElMessage.error("生成支付订单失败:" + e.message)
}) })
} }
const alipay = (row) => {
payName.value = "支付宝"
curPay.value = "alipay"
if (!user.value.id) {
showLoginDialog.value = true
return
}
if (row) {
curPayProduct.value = row
}
genPayQrcode()
}
//
const huPiPay = (row) => {
payName.value = payWays.value["hupi"]["name"] === "wechat" ? '微信' : '支付宝'
curPay.value = "hupi"
if (!user.value.id) {
showLoginDialog.value = true
return
}
if (row) {
curPayProduct.value = row
}
genPayQrcode()
}
const queryOrder = (orderNo) => {
httpPost("/api/payment/query", {order_no: orderNo}).then(res => {
if (res.data.status === 1) {
text.value = "扫码成功,请在手机上进行支付!"
queryOrder(orderNo)
} else if (res.data.status === 2) {
text.value = "支付成功,正在刷新页面"
setTimeout(() => location.reload(), 500)
} else {
//
if (activeOrderNo.value === orderNo) {
queryOrder(orderNo)
}
}
}).catch(e => {
ElMessage.error("查询支付状态失败:" + e.message)
})
}
const logout = function () {
httpGet('/api/user/logout').then(() => {
removeUserToken();
router.push('/login');
}).catch(() => {
ElMessage.error('注销失败!');
})
}
</script> </script>
<style lang="stylus"> <style lang="stylus">
.mobile-user-profile { @import "@/assets/css/mobile-profile.styl"
.content {
.van-field__label {
width 100px
text-align right
}
}
}
</style> </style>