家谱现有接口调试50%

This commit is contained in:
rain
2026-07-09 17:29:25 +08:00
commit 6050508144
262 changed files with 63354 additions and 0 deletions
+187
View File
@@ -0,0 +1,187 @@
.about-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.about-tags {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.about-tags span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255,253,248,.74);
box-shadow: 0 14px 32px rgba(72, 57, 38, .06);
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.about-tags span:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.about-seal {
position: relative;
min-height: 260px;
overflow: hidden;
display: grid;
place-items: center;
text-align: center;
color: #fff;
background: linear-gradient(155deg, var(--green-dark), var(--green));
}
.about-seal:before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(rgba(255,255,255,.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.08) 1px, transparent 1px);
background-size: 34px 34px;
opacity: .34;
}
.seal-orb {
position: absolute;
right: 76px;
top: 40px;
width: 110px;
height: 110px;
border-radius: 50%;
background: rgba(255,255,255,.16);
box-shadow: 0 0 40px rgba(255,255,255,.1);
animation: sealPulse 4s ease-in-out infinite;
}
.about-seal span {
position: relative;
z-index: 2;
width: 96px;
height: 96px;
margin-bottom: 20px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--red);
background: rgba(255,255,255,.94);
font-family: "SimSun", "Songti SC", serif;
font-size: 48px;
font-weight: 800;
transition: transform .24s ease, background .24s ease;
}
.about-seal:hover span {
transform: rotate(-8deg) scale(1.08);
}
.about-seal p {
position: relative;
z-index: 2;
margin: 0;
color: rgba(255,255,255,.84);
font-size: 22px;
}
.values-section {
position: relative;
overflow: hidden;
}
.values-section:before {
content: "";
position: absolute;
left: 50%;
top: 58px;
width: 520px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%);
transform: translateX(-50%);
}
.value-card {
min-height: 190px;
position: relative;
overflow: hidden;
}
.value-card:after {
content: "";
position: absolute;
left: -42%;
top: 0;
width: 36%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.54), transparent);
transform: skewX(-18deg);
transition: left .56s ease;
}
.value-card:hover:after {
left: 112%;
}
.value-icon {
width: 48px;
height: 48px;
margin-bottom: 18px;
border-radius: 14px;
display: grid;
place-items: center;
color: var(--red);
background: #f7e4dc;
font-family: "SimSun", "Songti SC", serif;
font-size: 22px;
font-weight: 900;
transition: transform .26s ease, background .26s ease, color .26s ease;
}
.value-card:hover .value-icon {
color: #fff;
background: var(--red);
transform: rotate(-6deg) scale(1.08);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes sealPulse {
0%, 100% { opacity: .55; transform: scale(.92); }
50% { opacity: .9; transform: scale(1.08); }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
}
+258
View File
@@ -0,0 +1,258 @@
.app-hero .container {
min-height: 500px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.hero-buttons {
display: flex;
gap: 16px;
flex-wrap: wrap;
margin-top: 28px;
}
.app-stats {
display: flex;
gap: 12px;
margin-top: 24px;
flex-wrap: wrap;
}
.app-stats span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255,253,248,.74);
box-shadow: 0 14px 32px rgba(72, 57, 38, .06);
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.app-stats span:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.app-shot-wrap {
position: relative;
min-height: 390px;
display: flex;
align-items: center;
justify-content: center;
gap: 30px;
overflow: hidden;
background:
radial-gradient(circle at 18% 20%, rgba(200,59,50,.12), transparent 24%),
radial-gradient(circle at 80% 24%, rgba(71,111,99,.16), transparent 30%),
linear-gradient(135deg, #f7efe2, #edf4ef);
}
.app-shot-wrap:before {
content: "";
position: absolute;
inset: 22px;
border: 1px solid rgba(224, 211, 189, .62);
border-radius: 22px;
pointer-events: none;
}
.app-orbit {
position: absolute;
right: 64px;
top: 44px;
width: 96px;
height: 96px;
border-radius: 50%;
background: rgba(196, 146, 69, .18);
animation: appOrbit 4.2s ease-in-out infinite;
}
.phone-shot {
position: relative;
z-index: 2;
width: 185px;
height: 380px;
object-fit: cover;
object-position: top center;
border: 8px solid #252b28;
border-radius: 28px;
box-shadow: 0 26px 60px rgba(33, 28, 22, .2);
transition: transform .3s ease, box-shadow .3s ease;
}
.phone-shot.first {
animation: phoneFloatA 4s ease-in-out infinite;
}
.phone-shot.second {
transform: translateY(28px);
animation: phoneFloatB 4s ease-in-out infinite;
}
.app-shot-wrap:hover .phone-shot {
box-shadow: 0 34px 70px rgba(33, 28, 22, .26);
}
.app-feature-section {
position: relative;
overflow: hidden;
}
.app-feature-section:before {
content: "";
position: absolute;
left: 50%;
top: 58px;
width: 520px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%);
transform: translateX(-50%);
}
.app-feature-card {
min-height: 190px;
position: relative;
overflow: hidden;
}
.app-feature-card:after {
content: "";
position: absolute;
left: -42%;
top: 0;
width: 36%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.54), transparent);
transform: skewX(-18deg);
transition: left .56s ease;
}
.app-feature-card:hover:after {
left: 112%;
}
.app-icon {
width: 48px;
height: 48px;
margin-bottom: 18px;
border-radius: 14px;
display: grid;
place-items: center;
color: var(--red);
background: #f7e4dc;
font-family: "SimSun", "Songti SC", serif;
font-size: 22px;
font-weight: 900;
transition: transform .26s ease, background .26s ease, color .26s ease;
}
.app-feature-card:hover .app-icon {
color: #fff;
background: var(--red);
transform: rotate(-6deg) scale(1.08);
}
.app-promotion-section {
/* 应用推广列表承载后端推广内容,不在 JS 中写样式 */
background: #fffdf8;
}
.promotion-list {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 18px;
}
.promotion-card {
overflow: hidden;
border: 1px solid var(--line);
border-radius: 8px;
background: #fffaf0;
box-shadow: 0 18px 42px rgba(57, 48, 36, .08);
transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease;
}
.promotion-card:hover {
border-color: rgba(196, 146, 69, .42);
box-shadow: 0 24px 54px rgba(57, 48, 36, .12);
transform: translateY(-4px);
}
.promotion-card img {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
display: block;
background: #f3eadc;
}
.promotion-card div {
padding: 18px;
}
.promotion-card h3 {
margin-bottom: 8px;
color: var(--ink);
font-size: 22px;
}
.promotion-card p {
margin: 0 0 12px;
color: var(--muted);
line-height: 1.75;
}
.promotion-card span {
color: var(--red);
font-weight: 900;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes phoneFloatA {
0%, 100% { transform: translateY(0) rotate(-1deg); }
50% { transform: translateY(-8px) rotate(1deg); }
}
@keyframes phoneFloatB {
0%, 100% { transform: translateY(28px) rotate(1deg); }
50% { transform: translateY(18px) rotate(-1deg); }
}
@keyframes appOrbit {
0%, 100% { opacity: .5; transform: scale(.92); }
50% { opacity: 1; transform: scale(1.08); }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
.phone-shot.second { display: none; }
.promotion-list { grid-template-columns: 1fr; }
}
+153
View File
@@ -0,0 +1,153 @@
.article-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.article-meta {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.article-meta span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255,253,248,.74);
font-weight: 800;
}
.article-cover-detail {
position: relative;
overflow: hidden;
display: grid;
place-items: center;
text-align: center;
color: #fff;
background:
radial-gradient(circle at 72% 24%, rgba(255,255,255,.35) 0 9%, transparent 10%),
linear-gradient(135deg, #e7c89e, #b94135 48%, #143d57);
}
.article-cover-detail:after {
content: "";
position: absolute;
left: -40%;
top: 0;
width: 34%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.3), transparent);
transform: skewX(-18deg);
transition: left .58s ease;
}
.article-cover-detail:hover:after { left: 112%; }
.article-cover-detail span {
width: 86px;
height: 86px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--red);
background: rgba(255,253,248,.94);
font-family: "SimSun", "Songti SC", serif;
font-size: 44px;
font-weight: 800;
}
.article-cover-detail p {
margin: 18px 0 0;
color: rgba(255,255,255,.9);
font-size: 22px;
}
.article-layout {
display: grid;
grid-template-columns: minmax(0, 820px) 300px;
gap: 28px;
align-items: start;
}
.article-content,
.article-side {
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255,253,248,.82);
box-shadow: 0 20px 54px rgba(57, 48, 36, .08);
}
.article-content {
padding: 42px;
}
.article-content p {
color: #5f665f;
font-size: 18px;
line-height: 2;
}
.article-content h2 {
margin-top: 34px;
font-size: 32px;
}
.article-side {
position: sticky;
top: 108px;
padding: 24px;
}
.article-side a {
display: block;
margin-top: 12px;
padding: 14px 16px;
border: 1px solid var(--line);
border-radius: 14px;
color: #6f4a37;
background: #fbf7ed;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.article-side a:hover {
color: #fff;
background: var(--green);
transform: translateX(5px);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover { --lift: -6px; }
@media (max-width: 980px) {
.article-layout { grid-template-columns: 1fr; }
.article-side { position: static; }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
.article-content { padding: 28px; }
}
+210
View File
@@ -0,0 +1,210 @@
.create-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.create-tags {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.create-tags span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255,253,248,.74);
box-shadow: 0 14px 32px rgba(72, 57, 38, .06);
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.create-tags span:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.process-card {
position: relative;
overflow: hidden;
display: grid;
align-content: center;
}
.process-card:before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(rgba(196,146,69,.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(196,146,69,.06) 1px, transparent 1px);
background-size: 34px 34px;
opacity: .72;
}
.process-card h3,
.process-card .step-list {
position: relative;
z-index: 2;
}
.process-orb {
position: absolute;
right: -70px;
top: -80px;
width: 220px;
height: 220px;
border-radius: 50%;
background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%);
animation: createPulse 4s ease-in-out infinite;
}
.step-list {
display: grid;
gap: 16px;
margin-top: 18px;
}
.step-list p {
display: flex;
gap: 14px;
align-items: center;
margin: 0;
color: var(--muted);
transition: transform .22s ease, color .22s ease;
}
.step-list p:hover {
color: var(--green-dark);
transform: translateX(6px);
}
.step-list b {
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
color: #fff;
background: var(--green);
flex: 0 0 auto;
box-shadow: 0 12px 28px rgba(71, 111, 99, .18);
transition: transform .22s ease, background .22s ease;
}
.step-list p:hover b {
background: var(--red);
transform: scale(1.08);
}
.create-form-section {
position: relative;
overflow: hidden;
}
.create-form-section:before {
content: "";
position: absolute;
left: 50%;
top: 58px;
width: 520px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%);
transform: translateX(-50%);
}
.create-form {
max-width: 720px;
position: relative;
overflow: hidden;
padding: 36px;
}
.form-ornament {
position: absolute;
right: -82px;
top: -88px;
width: 220px;
height: 220px;
border-radius: 50%;
background: radial-gradient(circle, rgba(71, 111, 99, .12), transparent 66%);
}
.create-form h2,
.create-form .form-note,
.create-form .form-grid {
position: relative;
z-index: 2;
}
.form-note {
margin-bottom: 22px;
color: var(--muted);
line-height: 1.8;
}
.create-form .input,
.create-form textarea {
transition: border-color .22s ease, box-shadow .22s ease, background .22s ease, transform .22s ease;
}
.create-region-picker {
/* 创建家谱页地区选择器占满表单宽度,编码通过隐藏字段提交 */
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.create-form .input:focus,
.create-form textarea:focus {
outline: none;
border-color: rgba(200, 59, 50, .62);
background: #fffdf8;
box-shadow: 0 0 0 4px rgba(200, 59, 50, .08);
transform: translateY(-2px);
}
.submit-btn {
width: 100%;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes createPulse {
0%, 100% { opacity: .5; transform: scale(.92); }
50% { opacity: 1; transform: scale(1.08); }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
.create-region-picker { grid-template-columns: 1fr; }
}
+213
View File
@@ -0,0 +1,213 @@
.culture-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.culture-stats {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.culture-stats span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255,253,248,.74);
box-shadow: 0 14px 32px rgba(72, 57, 38, .06);
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.culture-stats span:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.culture-quote {
position: relative;
min-height: 260px;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
color: #fff;
background:
linear-gradient(180deg, rgba(33,63,56,.18), rgba(33,63,56,.86)),
linear-gradient(135deg, #476f63, #c49245 50%, #c83b32);
}
.culture-quote:before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(rgba(255,255,255,.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.08) 1px, transparent 1px);
background-size: 34px 34px;
opacity: .34;
}
.quote-moon {
position: absolute;
right: 72px;
top: 34px;
width: 92px;
height: 92px;
border-radius: 50%;
background: rgba(255,255,255,.32);
box-shadow: 0 0 34px rgba(255,255,255,.12);
animation: moonFloat 4s ease-in-out infinite;
}
.culture-quote span {
position: relative;
z-index: 2;
width: 64px;
height: 64px;
margin-bottom: 24px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--red);
background: rgba(255,255,255,.94);
font-family: "SimSun", "Songti SC", serif;
font-size: 30px;
font-weight: 800;
transition: transform .24s ease, background .24s ease;
}
.culture-quote:hover span {
transform: rotate(-8deg) scale(1.08);
}
.culture-quote p {
position: relative;
z-index: 2;
margin: 0;
color: rgba(255,255,255,.86);
font-size: 22px;
line-height: 1.7;
}
.article-section {
position: relative;
overflow: hidden;
}
.article-section:before {
content: "";
position: absolute;
left: 50%;
top: 58px;
width: 520px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%);
transform: translateX(-50%);
}
.article-card {
position: relative;
overflow: hidden;
border-radius: 18px;
background: #fff;
border: 1px solid var(--line);
box-shadow: 0 18px 48px rgba(57, 48, 36, .07);
transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease;
}
.article-card:hover {
border-color: rgba(196, 146, 69, .42);
box-shadow: 0 24px 60px rgba(57, 48, 36, .11);
}
.article-cover {
position: relative;
height: 190px;
overflow: hidden;
background:
radial-gradient(circle at 74% 28%, rgba(255,255,255,.7) 0 8%, transparent 9%),
linear-gradient(135deg, #e7c89e, #b94135 48%, #143d57);
}
.article-cover:after {
content: "";
position: absolute;
left: -42%;
top: 0;
width: 36%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.34), transparent);
transform: skewX(-18deg);
transition: left .58s ease;
}
.article-card:hover .article-cover:after {
left: 112%;
}
.article-cover span {
position: absolute;
left: 22px;
bottom: 20px;
padding: 7px 12px;
border-radius: 999px;
color: var(--green-dark);
background: rgba(255,253,248,.86);
font-weight: 800;
font-size: 13px;
}
.article-card:nth-child(2) .article-cover {
background: linear-gradient(135deg, #dfd1b5, #6b7f70 48%, #26302c);
}
.article-card:nth-child(3) .article-cover {
background: linear-gradient(135deg, #f0d8ac, #c49245 45%, #7d342d);
}
.article-body { padding: 24px; }
.article-body h3 { transition: color .22s ease; }
.article-card:hover .article-body h3 { color: var(--red); }
.article-body p { color: var(--muted); line-height: 1.75; }
.article-body span { color: #9a8f82; font-size: 14px; }
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes moonFloat {
0%, 100% { transform: translateY(0) scale(1); opacity: .72; }
50% { transform: translateY(-8px) scale(1.04); opacity: .92; }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
}
+86
View File
@@ -0,0 +1,86 @@
.download-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.detail-actions {
display: flex;
gap: 14px;
margin-top: 30px;
flex-wrap: wrap;
}
.qr-card {
display: grid;
place-items: center;
text-align: center;
}
.qr-box {
width: 170px;
height: 170px;
padding: 16px;
border-radius: 24px;
background:
linear-gradient(90deg, var(--green) 12px, transparent 12px) 0 0 / 32px 32px,
linear-gradient(var(--green) 12px, transparent 12px) 0 0 / 32px 32px,
#fffdf8;
border: 1px solid var(--line);
display: grid;
place-items: center;
box-shadow: inset 0 0 0 12px #fffdf8;
}
.qr-box span {
width: 62px;
height: 62px;
border-radius: 50%;
display: grid;
place-items: center;
color: #fff;
background: var(--red);
font-weight: 900;
}
.qr-card p,
.download-card p {
color: var(--muted);
line-height: 1.8;
}
.download-card {
transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease;
}
.download-card:hover {
border-color: rgba(196, 146, 69, .42);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover { --lift: -6px; }
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
}
+160
View File
@@ -0,0 +1,160 @@
.page-family-detail [data-family-detail].is-loading {
/* 详情接口加载时只改变透明度,具体状态由公共 is-loading 类控制 */
opacity: .82;
}
.detail-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.detail-actions {
display: flex;
gap: 14px;
margin-top: 30px;
flex-wrap: wrap;
}
.family-summary {
position: relative;
overflow: hidden;
display: grid;
align-content: center;
gap: 24px;
color: #fff;
background:
linear-gradient(180deg, rgba(33,63,56,.18), rgba(33,63,56,.88)),
linear-gradient(135deg, #476f63, #c49245 52%, #8f4634);
}
.family-summary:before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(rgba(255,255,255,.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.08) 1px, transparent 1px);
background-size: 34px 34px;
opacity: .38;
}
.family-summary > * {
position: relative;
z-index: 2;
}
.summary-badge {
width: fit-content;
padding: 8px 14px;
border-radius: 999px;
color: var(--green-dark);
background: rgba(255,253,248,.88);
font-weight: 800;
}
.summary-grid {
/* 接口统计数据会替换这里的四个格子,固定列数避免加载后跳动 */
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.summary-grid span {
min-height: 84px;
padding: 16px;
border-radius: 16px;
display: grid;
place-items: center;
background: rgba(255,255,255,.13);
border: 1px solid rgba(255,255,255,.18);
}
.summary-grid b {
display: block;
font-size: 30px;
}
.detail-layout {
/* 详情页侧栏和正文为本页独有布局,不放入公共样式 */
display: grid;
grid-template-columns: 330px 1fr;
gap: 26px;
align-items: start;
}
.detail-panel,
.content-card,
.mini-card {
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255,253,248,.78);
box-shadow: 0 20px 54px rgba(57, 48, 36, .08);
}
.detail-panel {
padding: 26px;
position: sticky;
top: 108px;
}
.detail-panel p,
.content-card p,
.mini-card p {
color: var(--muted);
line-height: 1.8;
}
.detail-main {
display: grid;
gap: 24px;
}
.content-card {
padding: 30px;
}
.mini-card {
min-height: 170px;
padding: 24px;
transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease;
}
.mini-card:hover {
border-color: rgba(196, 146, 69, .42);
box-shadow: 0 24px 60px rgba(57, 48, 36, .11);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover { --lift: -6px; }
@media (max-width: 900px) {
.detail-layout { grid-template-columns: 1fr; }
.detail-panel { position: static; }
.summary-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
}
+27
View File
@@ -0,0 +1,27 @@
/* 找回密码页只保留单页装饰,公共认证卡片样式在 public.css */
.recover-card {
position: relative;
overflow: hidden;
}
.recover-card:before {
content: "";
position: absolute;
right: -80px;
top: -90px;
width: 220px;
height: 220px;
border-radius: 50%;
background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 64%);
}
.recover-card > * {
position: relative;
z-index: 2;
}
@media (max-width: 620px) {
.code-row {
grid-template-columns: 1fr;
}
}
+265
View File
@@ -0,0 +1,265 @@
.page-genealogy {
background: #fbf7ed;
}
.genealogy-hero .container {
min-height: 470px;
padding-top: 76px;
padding-bottom: 76px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 136px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.genealogy-stats {
display: flex;
gap: 12px;
margin-top: 28px;
padding: 8px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 18px;
background: rgba(255,253,248,.74);
box-shadow: 0 16px 36px rgba(72, 57, 38, .07);
}
.genealogy-stats div {
min-width: 118px;
padding: 12px 16px;
border-radius: 12px;
}
.genealogy-stats div + div {
border-left: 1px solid rgba(218, 201, 174, .78);
}
.genealogy-stats strong {
display: block;
color: var(--green);
font-size: 24px;
line-height: 1.1;
}
.genealogy-stats span {
color: #6d766f;
font-size: 14px;
}
.tree-preview {
position: relative;
min-height: 300px;
display: grid;
gap: 14px;
align-content: center;
overflow: hidden;
background:
linear-gradient(90deg, rgba(255,253,248,.95), rgba(255,253,248,.78)),
radial-gradient(circle at 78% 22%, rgba(196,146,69,.2), transparent 28%);
transform-style: preserve-3d;
}
.tree-glow {
position: absolute;
right: -70px;
top: -80px;
width: 210px;
height: 210px;
border-radius: 50%;
background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%);
animation: treePulse 4.2s ease-in-out infinite;
}
.tree-row {
position: relative;
z-index: 2;
display: flex;
justify-content: center;
gap: 16px;
flex-wrap: wrap;
}
.tree-row span {
min-width: 110px;
padding: 14px 18px;
border: 1px solid #dac9ae;
border-radius: 14px;
text-align: center;
color: var(--green-dark);
background: rgba(255,250,241,.9);
box-shadow: 0 12px 28px rgba(69, 53, 35, .06);
font-weight: 800;
transition: transform .24s ease, color .24s ease, background .24s ease, box-shadow .24s ease;
}
.tree-preview:hover .tree-row span {
animation: nodeFloat 1.9s ease-in-out infinite;
}
.tree-row span:hover {
color: #fff;
background: var(--green);
box-shadow: 0 16px 32px rgba(71, 111, 99, .22);
}
.tree-row.branch span:nth-child(2) { animation-delay: .08s; }
.tree-row.branch span:nth-child(3) { animation-delay: .16s; }
.tree-row.generation span:nth-child(2) { animation-delay: .08s; }
.tree-row.generation span:nth-child(3) { animation-delay: .16s; }
.tree-row.generation span:nth-child(4) { animation-delay: .24s; }
.tree-line {
position: relative;
z-index: 1;
width: min(360px, 70%);
height: 1px;
margin: 0 auto;
background: linear-gradient(90deg, transparent, rgba(196, 146, 69, .5), transparent);
}
.tree-line.short {
width: min(500px, 86%);
}
.capability-section {
position: relative;
overflow: hidden;
}
.capability-section:before {
content: "";
position: absolute;
left: 50%;
top: 58px;
width: 520px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%);
transform: translateX(-50%);
pointer-events: none;
}
.feature-card {
min-height: 210px;
position: relative;
overflow: hidden;
}
.feature-card:after {
content: "";
position: absolute;
left: -40%;
top: 0;
width: 36%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.48), transparent);
transform: skewX(-18deg);
transition: left .56s ease;
}
.feature-card:hover:after {
left: 110%;
}
.icon-word {
width: 48px;
height: 48px;
margin-bottom: 18px;
border-radius: 14px;
display: grid;
place-items: center;
color: var(--red);
background: #f7e4dc;
font-size: 22px;
font-weight: 900;
transition: transform .26s ease, background .26s ease, color .26s ease;
}
.feature-card:hover .icon-word {
color: #fff;
background: var(--red);
transform: rotate(-6deg) scale(1.08);
}
.coedit-section {
background:
radial-gradient(circle at 18% 20%, rgba(200, 59, 50, .08), transparent 24%),
#f7efe2;
}
.coedit-card {
min-height: 260px;
padding: 34px;
}
.coedit-card .btn {
margin-top: 12px;
}
.process-card {
min-height: 260px;
padding: 34px;
}
.step-list {
display: grid;
gap: 16px;
margin-top: 20px;
}
.step-list p {
display: flex;
gap: 14px;
align-items: center;
margin: 0;
color: var(--muted);
}
.step-list b {
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
color: #fff;
background: var(--green);
flex: 0 0 auto;
box-shadow: 0 12px 28px rgba(71, 111, 99, .18);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes nodeFloat {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
@keyframes treePulse {
0%, 100% { opacity: .5; transform: scale(.92); }
50% { opacity: 1; transform: scale(1.08); }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
.genealogy-stats { flex-direction: column; }
.genealogy-stats div + div { border-left: 0; border-top: 1px solid rgba(218, 201, 174, .78); }
}
+211
View File
@@ -0,0 +1,211 @@
.help-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.help-tags {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.help-tags span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255,253,248,.74);
box-shadow: 0 14px 32px rgba(72, 57, 38, .06);
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.help-tags span:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.help-card {
position: relative;
overflow: hidden;
display: grid;
align-content: center;
gap: 14px;
}
.help-card:before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(rgba(196,146,69,.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(196,146,69,.06) 1px, transparent 1px);
background-size: 34px 34px;
opacity: .72;
}
.help-card h3,
.help-card p,
.help-card .btn,
.help-icon {
position: relative;
z-index: 2;
}
.help-orb {
position: absolute;
right: -70px;
top: -80px;
width: 220px;
height: 220px;
border-radius: 50%;
background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%);
animation: helpPulse 4s ease-in-out infinite;
}
.help-icon {
width: 52px;
height: 52px;
border-radius: 16px;
display: grid;
place-items: center;
color: #fff;
background: var(--green);
font-size: 26px;
font-weight: 900;
box-shadow: 0 14px 32px rgba(71, 111, 99, .2);
transition: transform .26s ease, background .26s ease;
}
.help-card:hover .help-icon {
background: var(--red);
transform: rotate(-8deg) scale(1.08);
}
.faq-section {
position: relative;
overflow: hidden;
}
.faq-section:before {
content: "";
position: absolute;
left: 50%;
top: 58px;
width: 520px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%);
transform: translateX(-50%);
}
.faq-list {
/* 帮助文章接口会替换 FAQ 内容,固定列表容器宽度避免加载后跳动 */
position: relative;
max-width: 880px;
margin: 0 auto;
display: grid;
gap: 14px;
}
.faq-list details {
padding: 22px 24px;
border: 1px solid var(--line);
border-radius: 18px;
background: #fffdf8;
box-shadow: 0 12px 34px rgba(57, 48, 36, .06);
transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease, background .24s ease;
}
.faq-list.is-loading {
min-height: 220px;
}
.faq-list details:hover,
.faq-list details[open] {
border-color: rgba(196, 146, 69, .42);
box-shadow: 0 22px 54px rgba(57, 48, 36, .1);
}
.faq-list summary {
position: relative;
cursor: pointer;
color: var(--ink);
font-size: 20px;
font-weight: 800;
list-style: none;
padding-left: 34px;
}
.faq-list summary::-webkit-details-marker {
display: none;
}
.faq-list summary:before {
content: "";
position: absolute;
left: 0;
top: 8px;
width: 0;
height: 0;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
border-left: 10px solid var(--green-dark);
transition: transform .24s ease, border-left-color .24s ease;
}
.faq-list details[open] summary:before {
border-left-color: var(--red);
transform: rotate(90deg);
}
.faq-list p {
margin: 14px 0 0;
padding-left: 34px;
color: var(--muted);
line-height: 1.8;
animation: faqReveal .24s ease both;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes helpPulse {
0%, 100% { opacity: .5; transform: scale(.92); }
50% { opacity: 1; transform: scale(1.08); }
}
@keyframes faqReveal {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
}
+1398
View File
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
/* 加入家谱页只保留单页差异,公共认证卡片样式在 public.css */
.page-join-genealogy .auth-card {
width: min(500px, 100%);
}
.invite-box {
padding: 14px;
border: 1px dashed var(--gold);
border-radius: 22px;
background: #fffaf1;
}
.page-join-genealogy textarea.input {
/* 申请说明需要多行输入,仍复用公共输入框视觉 */
min-height: 112px;
resize: vertical;
}
+54
View File
@@ -0,0 +1,54 @@
/* 登录页只保留单页差异,公共认证卡片样式在 public.css */
.page-login .auth-card {
width: min(440px, 100%);
}
.login-mode-tabs {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
margin: 18px 0 16px;
padding: 4px;
border: 1px solid rgba(138, 64, 42, .14);
border-radius: 8px;
background: rgba(255, 255, 255, .72);
}
/* 登录方式按钮只负责状态呈现,切换逻辑在 auth-pages.js */
.login-mode-tab {
min-height: 38px;
border: 0;
border-radius: 6px;
background: transparent;
color: var(--muted);
font: inherit;
cursor: pointer;
}
.login-mode-tab.is-active {
background: var(--brand);
color: #fff;
box-shadow: 0 8px 20px rgba(138, 64, 42, .18);
}
.login-mode-panel[hidden] {
display: none;
}
.page-login .code-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 124px;
gap: 10px;
}
@media (max-width: 720px) {
.page-login .auth-layout {
align-items: start;
}
}
@media (max-width: 620px) {
.page-login .code-row {
grid-template-columns: 1fr;
}
}
+126
View File
@@ -0,0 +1,126 @@
.notice-hero .container {
min-height: 420px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.article-meta {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.article-meta span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255,253,248,.74);
font-weight: 800;
}
.notice-card {
display: grid;
place-items: center;
text-align: center;
color: #fff;
background: linear-gradient(145deg, var(--green-dark), var(--green));
}
.notice-card span {
width: 88px;
height: 88px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--red);
background: rgba(255,253,248,.94);
font-family: "SimSun", "Songti SC", serif;
font-size: 42px;
font-weight: 900;
}
.notice-card p {
margin: 18px 0 0;
color: rgba(255,255,255,.88);
font-size: 22px;
}
.notice-layout {
display: grid;
grid-template-columns: minmax(0, 820px) 300px;
gap: 28px;
align-items: start;
}
.notice-content,
.notice-side {
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255,253,248,.82);
box-shadow: 0 20px 54px rgba(57, 48, 36, .08);
}
.notice-content {
padding: 42px;
}
.notice-content p {
color: #5f665f;
font-size: 18px;
line-height: 2;
}
.notice-side {
padding: 24px;
}
.notice-side a {
display: block;
margin-top: 12px;
padding: 14px 16px;
border: 1px solid var(--line);
border-radius: 14px;
color: #6f4a37;
background: #fbf7ed;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.notice-side a:hover {
color: #fff;
background: var(--green);
transform: translateX(5px);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover { --lift: -6px; }
@media (max-width: 900px) {
.notice-layout { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
.hero-copy { padding-left: 0; }
.hero-copy:before { display: none; }
}
+243
View File
@@ -0,0 +1,243 @@
.plaza-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.plaza-stats {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.plaza-stats span {
padding: 12px 16px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 16px;
color: #6d766f;
background: rgba(255, 253, 248, .74);
box-shadow: 0 14px 32px rgba(72, 57, 38, .06);
}
.plaza-stats b {
margin-right: 6px;
color: var(--green);
font-size: 22px;
}
.search-card {
position: relative;
display: grid;
gap: 20px;
align-content: center;
overflow: hidden;
}
.search-card h3 {
position: relative;
z-index: 2;
}
.search-ornament {
position: absolute;
right: -70px;
top: -84px;
width: 220px;
height: 220px;
border-radius: 50%;
background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%);
animation: plazaPulse 4s ease-in-out infinite;
}
.search-toolbar {
position: relative;
z-index: 2;
padding: 8px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
background: rgba(255, 250, 241, .82);
}
.search-card .input {
flex: 1 1 280px;
border: 0;
background: transparent;
}
.search-card .tag-row {
position: relative;
z-index: 2;
}
.family-card.featured {
min-height: 300px;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: flex-end;
color: #fff;
background:
linear-gradient(180deg, rgba(28, 48, 45, .18), rgba(21, 55, 51, .84)),
linear-gradient(135deg, #476f63, #a95c3e);
border-radius: 18px;
padding: 28px;
box-shadow: 0 22px 58px rgba(42, 65, 59, .16);
}
.family-card.featured:before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(rgba(255, 255, 255, .08) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, .08) 1px, transparent 1px);
background-size: 34px 34px;
opacity: .45;
}
.family-card.featured h3,
.family-card.featured p,
.family-card.featured .tag-row,
.family-badge {
position: relative;
z-index: 2;
}
.family-card.featured p {
color: rgba(255, 255, 255, .82);
}
.family-badge {
width: fit-content;
margin-bottom: auto;
padding: 7px 12px;
border-radius: 999px;
color: var(--green-dark);
background: rgba(255, 253, 248, .86);
font-weight: 800;
font-size: 13px;
}
.family-card-shine {
position: absolute;
left: -40%;
top: 0;
width: 34%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .28), transparent);
transform: skewX(-18deg);
transition: left .6s ease;
}
.family-card.featured:hover .family-card-shine {
left: 112%;
}
.family-lite {
min-height: 300px;
position: relative;
overflow: hidden;
}
.family-lite:after {
content: "";
position: absolute;
right: -60px;
bottom: -70px;
width: 170px;
height: 170px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .13), transparent 68%);
transition: transform .28s ease, opacity .28s ease;
}
.family-lite:hover:after {
transform: scale(1.25);
opacity: .8;
}
.family-icon {
width: 52px;
height: 52px;
margin-bottom: 24px;
border-radius: 16px;
display: grid;
place-items: center;
color: var(--red);
background: #f7e4dc;
font-family: "SimSun", "Songti SC", serif;
font-size: 26px;
font-weight: 800;
transition: transform .26s ease, background .26s ease, color .26s ease;
}
.family-lite:hover .family-icon {
color: #fff;
background: var(--red);
transform: rotate(-6deg) scale(1.08);
}
.tag {
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.tag:hover {
color: #fff;
background: var(--green);
transform: translateY(-2px);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes plazaPulse {
0%,
100% {
opacity: .5;
transform: scale(.92);
}
50% {
opacity: 1;
transform: scale(1.08);
}
}
@media (max-width: 720px) {
.hero-copy {
padding-left: 0;
}
.hero-copy:before {
display: none;
}
.search-toolbar {
border-radius: 18px;
}
}
+722
View File
@@ -0,0 +1,722 @@
.page-profile-module {
background: #f8f4ec;
}
.module-hero {
position: relative;
overflow: hidden;
padding: 58px 0 42px;
background:
radial-gradient(circle at 76% 18%, rgba(196, 146, 69, .16), transparent 25%),
linear-gradient(125deg, #fffaf0 0%, #f5eadb 52%, #eaf2ee 100%);
}
.module-hero:before {
content: "";
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(138, 50, 43, .05) 1px, transparent 1px),
linear-gradient(90deg, rgba(138, 50, 43, .05) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
.module-hero .container {
position: relative;
z-index: 2;
}
.module-kicker {
width: fit-content;
margin-bottom: 18px;
padding: 8px 14px;
border-radius: 999px;
color: var(--red);
background: rgba(248, 229, 223, .92);
font-weight: 900;
}
.module-title-row {
display: flex;
justify-content: space-between;
gap: 22px;
align-items: flex-end;
}
.module-title-row h1 {
margin-bottom: 12px;
font-size: 48px;
}
.module-title-row p {
max-width: 780px;
margin: 0;
color: #636c65;
font-size: 17px;
line-height: 1.9;
}
.module-layout {
display: grid;
grid-template-columns: 280px minmax(0, 1fr);
gap: 24px;
align-items: start;
}
.module-nav,
.module-panel,
.module-card {
border: 1px solid rgba(224, 211, 189, .86);
border-radius: 18px;
background: rgba(255,253,248,.88);
box-shadow: 0 22px 58px rgba(57, 48, 36, .08);
}
.module-nav {
position: sticky;
top: 106px;
display: grid;
gap: 8px;
padding: 16px;
}
.module-nav a {
padding: 14px 16px;
border-radius: 14px;
color: #5e665f;
font-weight: 900;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.module-nav a:hover,
.module-nav a.active {
color: var(--red);
background: #f8e5df;
transform: translateX(4px);
}
.module-main {
display: grid;
gap: 20px;
}
.module-panel {
padding: 28px;
}
.module-panel h2 {
margin-bottom: 10px;
font-family: "SimSun", "Songti SC", serif;
font-size: 32px;
}
.module-panel > p {
color: var(--muted);
line-height: 1.85;
}
.module-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 18px;
}
.module-grid.two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.module-card {
position: relative;
overflow: hidden;
min-height: 170px;
padding: 22px;
transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease;
}
.module-action-card {
/* 按钮型模块卡片用于消息中心操作,保留卡片视觉但去掉浏览器按钮默认样式 */
width: 100%;
text-align: left;
cursor: pointer;
font: inherit;
}
.notification-row.is-unread {
/* 未读消息用轻量背景强调,不新增内联样式 */
border-color: rgba(200, 59, 50, .28);
background: #fff7f2;
}
.join-apply-row {
/* 加入申请行复用 module-row,只补充审核业务的按钮对齐 */
align-items: center;
}
.join-apply-row .bottom-actions {
justify-content: flex-end;
}
.generation-row {
/* 字辈谱行复用 module-row,补充按钮型 pill 的对齐和点击反馈 */
align-items: center;
}
.generation-row .pill {
border: 0;
cursor: pointer;
font: inherit;
}
.lineage-row {
/* 世系人物行使用 button 承载点击详情,保留列表卡片视觉 */
width: 100%;
text-align: left;
cursor: pointer;
font: inherit;
}
.lineage-row.is-selected {
border-color: rgba(200, 59, 50, .38);
background: #fff6f1;
}
.lineage-toolbar {
/* 搜索栏只做世系图页面差异布局,不放到 JS 内联样式 */
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 12px;
margin-bottom: 16px;
}
.lineage-toolbar input {
min-height: 46px;
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fffdf8;
color: var(--ink);
font: inherit;
outline: none;
}
.lineage-tree-wrap {
overflow-x: auto;
padding: 18px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fffdf8;
}
.lineage-tree-wrap.is-compact {
max-height: 420px;
}
.lineage-tree,
.lineage-tree ul {
/* 树结构只负责层级缩进,避免用 JS 计算位置 */
display: grid;
gap: 12px;
margin: 0;
padding-left: 24px;
list-style: none;
}
.lineage-tree {
padding-left: 0;
}
.lineage-node {
min-width: 168px;
padding: 12px 14px;
border: 1px solid rgba(224, 211, 189, .86);
border-radius: 8px;
background: #fff7f2;
color: var(--ink);
text-align: left;
cursor: pointer;
font: inherit;
}
.lineage-node strong,
.lineage-node span {
display: block;
}
.lineage-node span {
margin-top: 4px;
color: var(--muted);
font-size: 13px;
}
.member-admin-row {
/* 成员管理行保留列表结构,右侧放权限和移除操作 */
align-items: center;
}
.row-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: flex-end;
}
.pill.is-danger {
color: #8a322b;
background: #fde8e2;
}
.article-row {
/* 谱文列表行复用模块列表视觉,单独保留类名方便后续文章样式调整 */
align-items: center;
}
.feed-row {
/* 家族圈动态行右侧承载点赞和评论按钮 */
align-items: center;
}
.feed-row small {
display: block;
margin-top: 6px;
color: var(--muted);
}
.album-row,
.album-photo-row {
/* 相册和照片行使用独立类名,便于后续相册页单独调样式 */
align-items: center;
}
.album-photo-form {
margin-top: 18px;
}
.upload-control {
/* 公共上传按钮使用 label 触发文件选择,避免 JS 注入按钮样式 */
width: fit-content;
min-height: 42px;
display: inline-flex;
align-items: center;
justify-content: center;
margin-top: 10px;
padding: 10px 14px;
border-radius: 8px;
color: var(--red);
background: #f8e5df;
font-weight: 900;
cursor: pointer;
}
.upload-input {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
.upload-status {
margin: 8px 0 0;
color: var(--muted);
font-size: 14px;
}
.ceremony-row,
.ceremony-gift-row,
.merit-row {
/* 贺礼、献礼和功德记录行复用模块列表,仅补业务类名方便维护 */
align-items: center;
}
.ceremony-gift-form {
margin-top: 18px;
}
.growth-row {
/* 成长记录行复用模块列表,单独留类名方便后续调整 */
align-items: center;
}
.memo-row {
/* 备忘录行复用模块列表,单独留类名方便后续调整 */
align-items: center;
}
.vip-package-list {
/* 会员套餐列表用网格呈现,套餐卡片由接口数据渲染 */
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
margin: 18px 0;
}
.vip-package-card {
min-height: 188px;
padding: 20px;
border: 1px solid rgba(224, 211, 189, .86);
border-radius: 8px;
background: #fffdf8;
color: var(--ink);
text-align: left;
cursor: pointer;
font: inherit;
transition: border-color .22s ease, box-shadow .22s ease, transform .22s ease;
}
.vip-package-card:hover,
.vip-package-card.is-selected {
border-color: rgba(200, 59, 50, .48);
box-shadow: 0 18px 42px rgba(57, 48, 36, .1);
transform: translateY(-2px);
}
.vip-package-name,
.vip-package-card strong,
.vip-package-card small {
display: block;
}
.vip-package-name {
margin-bottom: 12px;
color: var(--red);
font-weight: 900;
}
.vip-package-card strong {
margin-bottom: 6px;
font-size: 30px;
}
.vip-package-card small {
color: var(--muted);
}
.vip-package-card p {
margin: 14px 0 0;
color: var(--muted);
line-height: 1.7;
}
.vip-order-form {
margin-top: 20px;
}
.vip-order-row {
/* 会员订单行只补业务类名,方便和其他 module-row 分开维护 */
align-items: center;
}
.security-form {
/* 安全设置表单复用编辑表单结构,只补模块间距 */
margin-top: 16px;
}
.security-danger {
border-color: rgba(138, 50, 43, .28);
background: #fff7f2;
}
.security-danger .editor-field input,
.security-danger .editor-field textarea {
background: #fffdf8;
}
.region-profile-form {
/* 个人资料地区表单使用公共地区选择器,样式集中在 CSS */
margin-top: 16px;
}
.profile-region-picker {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.profile-region-picker select {
min-height: 48px;
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fffdf8;
color: var(--ink);
font: inherit;
outline: none;
}
.profile-create-family-form {
/* 个人中心创建家谱页复用编辑表单,只保留页面级间距 */
margin-top: 16px;
}
.module-card:hover {
border-color: rgba(196, 146, 69, .42);
box-shadow: 0 24px 60px rgba(57, 48, 36, .11);
}
.module-card.is-selected {
border-color: rgba(200, 59, 50, .5);
background: #fff6f1;
box-shadow: 0 24px 60px rgba(200, 59, 50, .12);
}
.module-card:after {
content: "";
position: absolute;
inset: 0 auto 0 -42%;
width: 34%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.52), transparent);
transform: skewX(-18deg);
transition: left .54s ease;
}
.module-card:hover:after {
left: 112%;
}
.module-card .icon {
width: 46px;
height: 46px;
margin-bottom: 18px;
border-radius: 14px;
display: grid;
place-items: center;
color: var(--red);
background: #f8e5df;
font-weight: 900;
}
.module-card h3 {
margin-bottom: 8px;
font-size: 23px;
}
.module-card p {
color: var(--muted);
line-height: 1.75;
}
.module-list {
display: grid;
gap: 14px;
}
.module-row {
display: grid;
grid-template-columns: 1fr auto;
gap: 16px;
align-items: center;
padding: 18px 20px;
border: 1px solid var(--line);
border-radius: 15px;
background: #fffdf8;
}
.module-row h3 {
margin-bottom: 4px;
font-size: 22px;
}
.module-row p {
margin: 0;
color: var(--muted);
}
.pill {
padding: 7px 12px;
border-radius: 999px;
color: var(--red);
background: #f8e5df;
font-weight: 900;
white-space: nowrap;
}
.form-like {
display: grid;
overflow: hidden;
border: 1px solid var(--line);
border-radius: 16px;
background: #fffdf8;
}
.form-like p {
display: grid;
grid-template-columns: 170px 1fr auto;
gap: 16px;
margin: 0;
padding: 18px 20px;
border-bottom: 1px solid var(--line);
align-items: center;
}
.form-like p:last-child {
border-bottom: 0;
}
.form-like span {
color: var(--muted);
}
.form-like b {
color: var(--ink);
}
.bottom-actions {
display: flex;
gap: 14px;
flex-wrap: wrap;
}
.editor-form {
display: grid;
gap: 18px;
}
.profile-data-form {
/* 个人资料编辑表单只补充本页间距,基础控件继续复用 editor-form。 */
margin-top: 14px;
}
.editor-field {
display: grid;
gap: 10px;
}
.editor-field label {
color: var(--ink);
font-weight: 900;
}
.editor-field input,
.editor-field select,
.editor-field textarea {
width: 100%;
min-height: 48px;
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fffdf8;
color: var(--ink);
font: inherit;
outline: none;
transition: border-color .2s ease, box-shadow .2s ease;
}
.editor-field textarea {
min-height: 160px;
resize: vertical;
}
.editor-field input:focus,
.editor-field select:focus,
.editor-field textarea:focus {
border-color: rgba(200, 59, 50, .45);
box-shadow: 0 0 0 4px rgba(200, 59, 50, .08);
}
.editor-two {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.profile-form-actions {
/* 保存状态跟随按钮排列,避免用 JS 写入临时样式。 */
align-items: center;
}
.form-status {
color: var(--muted);
font-weight: 800;
}
.generation-batch-form {
/* 批量字辈表单复用编辑表单,只补充本页预览前的间距。 */
margin-top: 14px;
}
.generation-batch-check {
display: flex;
gap: 10px;
align-items: center;
color: var(--muted);
font-weight: 800;
}
.generation-batch-check input {
width: 18px;
height: 18px;
accent-color: var(--red);
}
.generation-batch-actions {
align-items: center;
}
.generation-batch-preview {
margin-top: 18px;
}
.upload-box {
min-height: 136px;
border: 1px dashed rgba(138, 50, 43, .28);
border-radius: 8px;
display: grid;
place-items: center;
color: var(--red);
background: #fff8f2;
font-weight: 900;
cursor: pointer;
}
.switch-row {
display: flex;
gap: 18px;
flex-wrap: wrap;
align-items: center;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -5px;
}
@media (max-width: 1060px) {
.module-layout,
.module-grid,
.module-grid.two,
.vip-package-list {
grid-template-columns: 1fr;
}
.module-nav {
position: static;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 720px) {
.module-title-row {
display: grid;
}
.module-title-row h1 {
font-size: 38px;
}
.module-nav {
grid-template-columns: 1fr;
}
.form-like p,
.module-row,
.editor-two,
.profile-region-picker {
grid-template-columns: 1fr;
}
}
+613
View File
@@ -0,0 +1,613 @@
.page-profile {
background: #f8f4ec;
}
.profile-hero {
position: relative;
overflow: hidden;
padding: 62px 0 34px;
background:
radial-gradient(circle at 78% 12%, rgba(196, 146, 69, .18), transparent 25%),
linear-gradient(125deg, #fffaf0 0%, #f4eadb 48%, #eaf2ee 100%);
}
.profile-hero:before {
content: "";
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(138, 50, 43, .05) 1px, transparent 1px),
linear-gradient(90deg, rgba(138, 50, 43, .05) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
.profile-hero-grid {
position: relative;
z-index: 2;
display: grid;
grid-template-columns: minmax(0, 1fr) 420px;
gap: 24px;
align-items: stretch;
}
.profile-card,
.profile-stats,
.panel,
.side-card {
border: 1px solid rgba(224, 211, 189, .84);
border-radius: 18px;
background: rgba(255, 253, 248, .86);
box-shadow: 0 22px 58px rgba(57, 48, 36, .08);
}
.profile-card {
display: grid;
grid-template-columns: 116px 1fr;
gap: 28px;
padding: 34px;
align-items: center;
}
.avatar-wrap {
position: relative;
width: 108px;
height: 108px;
}
.avatar {
width: 108px;
height: 108px;
border-radius: 28px;
display: grid;
place-items: center;
color: #fff;
background:
radial-gradient(circle at 30% 24%, rgba(255, 255, 255, .38), transparent 24%),
linear-gradient(145deg, var(--red), #ee8d60 52%, var(--green));
font-family: "SimSun", "Songti SC", serif;
font-size: 46px;
font-weight: 900;
box-shadow: 0 18px 40px rgba(200, 59, 50, .18);
}
.avatar-wrap span {
position: absolute;
right: -4px;
bottom: -4px;
width: 28px;
height: 28px;
border: 5px solid #fffdf8;
border-radius: 50%;
background: #31bf6b;
}
.profile-info h1 {
margin-bottom: 12px;
font-size: 44px;
}
.profile-info p {
max-width: 720px;
color: #636c65;
font-size: 17px;
line-height: 1.9;
}
.profile-tags,
.mini-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.profile-tags span,
.mini-actions span {
padding: 7px 12px;
border-radius: 999px;
color: var(--red);
background: #f8e5df;
font-size: 14px;
font-weight: 800;
}
.profile-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
padding: 22px;
gap: 14px;
}
.profile-stats div {
min-height: 116px;
padding: 20px;
border-radius: 14px;
display: grid;
align-content: center;
background: rgba(247, 239, 226, .72);
}
.profile-stats strong {
color: var(--green);
font-size: 34px;
line-height: 1;
}
.profile-stats span,
.panel-head p,
.data-list span,
.todo-list span,
.family-card p,
.feature-tile p,
.content-tile p {
color: var(--muted);
line-height: 1.75;
}
.dashboard-section {
padding-top: 42px;
}
.dashboard-layout {
display: grid;
grid-template-columns: 300px minmax(0, 1fr);
gap: 24px;
align-items: start;
}
.dashboard-side {
position: sticky;
top: 106px;
display: grid;
gap: 20px;
}
.side-card,
.panel {
padding: 26px;
}
.side-card h2,
.panel h2 {
margin-bottom: 8px;
font-family: "SimSun", "Songti SC", serif;
font-size: 30px;
}
.user-actions {
display: grid;
gap: 10px;
}
.user-actions a,
.todo-list a {
display: block;
padding: 15px 16px;
border: 1px solid var(--line);
border-radius: 14px;
background: #fbf7ed;
transition: transform .22s ease, border-color .22s ease, background .22s ease, color .22s ease;
}
.user-actions a:hover,
.todo-list a:hover {
transform: translateX(5px);
border-color: rgba(71, 111, 99, .28);
background: #eef4ef;
}
.user-actions span,
.todo-list b {
display: block;
color: var(--ink);
font-weight: 900;
}
.user-actions b {
display: block;
margin-top: 4px;
color: var(--muted);
font-size: 13px;
}
.logout-link span {
color: var(--red);
}
.card-head,
.panel-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 18px;
margin-bottom: 22px;
}
.card-head span {
padding: 6px 11px;
border-radius: 999px;
color: #fff;
background: var(--red);
font-weight: 800;
}
.todo-list,
.dashboard-main {
display: grid;
gap: 18px;
}
.panel-head.compact {
margin-bottom: 18px;
}
.panel-head h2 {
margin-bottom: 6px;
}
.family-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 18px;
}
.family-card {
display: grid;
grid-template-columns: 112px 1fr;
gap: 20px;
min-height: 190px;
padding: 20px;
border: 1px solid var(--line);
border-radius: 16px;
background: #fffdf8;
transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease;
}
.family-card:hover,
.feature-tile:hover,
.content-tile:hover {
border-color: rgba(196, 146, 69, .42);
box-shadow: 0 22px 52px rgba(57, 48, 36, .1);
}
.book-cover {
min-height: 150px;
border-radius: 10px;
display: grid;
place-items: center;
color: #fffdf8;
background:
linear-gradient(90deg, transparent calc(100% - 16px), rgba(255, 255, 255, .7) calc(100% - 15px), transparent calc(100% - 14px)),
linear-gradient(135deg, #0f405c, #092c42);
font-family: "SimSun", "Songti SC", serif;
font-size: 22px;
line-height: 1.7;
text-align: center;
box-shadow: inset -12px 0 0 rgba(255, 255, 255, .08);
}
.book-cover.alt {
background:
linear-gradient(90deg, transparent calc(100% - 16px), rgba(255, 255, 255, .7) calc(100% - 15px), transparent calc(100% - 14px)),
linear-gradient(135deg, #173c35, #785f35);
}
.family-card h3,
.feature-tile h3,
.content-tile h3 {
margin-bottom: 8px;
font-size: 24px;
}
.feature-grid,
.content-grid,
.service-grid {
display: grid;
gap: 16px;
}
.management-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.feature-tile,
.content-tile {
position: relative;
overflow: hidden;
min-height: 170px;
padding: 22px;
border: 1px solid var(--line);
border-radius: 16px;
background: #fffdf8;
transition: transform .22s ease, border-color .22s ease, box-shadow .22s ease;
}
.feature-tile span,
.content-tile span {
width: 48px;
height: 48px;
margin-bottom: 18px;
border-radius: 14px;
display: grid;
place-items: center;
color: var(--red);
background: #f7e4dc;
font-weight: 900;
}
.feature-tile:after,
.content-tile:after {
content: "";
position: absolute;
inset: 0 auto 0 -42%;
width: 34%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .52), transparent);
transform: skewX(-18deg);
transition: left .54s ease;
}
.feature-tile:hover:after,
.content-tile:hover:after {
left: 112%;
}
.content-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.content-tile.large {
grid-column: span 2;
color: #fff;
background: linear-gradient(145deg, var(--green-dark), var(--green));
}
.content-tile.large p,
.content-tile.large h3 {
color: rgba(255, 255, 255, .86);
}
.content-tile.large span {
color: var(--green-dark);
background: rgba(255, 253, 248, .9);
}
.profile-data-layout {
display: grid;
grid-template-columns: 1.15fr .85fr;
gap: 18px;
}
.link-btn {
flex: 0 0 auto;
padding: 9px 14px;
border-radius: 999px;
color: var(--red);
background: #f8e5df;
font-weight: 900;
}
.data-list {
display: grid;
border-top: 1px solid var(--line);
}
.data-list p {
display: grid;
grid-template-columns: 150px 1fr;
gap: 16px;
margin: 0;
padding: 16px 0;
border-bottom: 1px solid var(--line);
}
.data-list b {
color: var(--ink);
}
.service-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.service-grid a {
min-height: 88px;
padding: 18px;
border-radius: 14px;
display: grid;
place-items: center;
color: #6f4a37;
background: #fbf7ed;
border: 1px solid var(--line);
font-weight: 900;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.service-grid a:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -5px;
}
.logout-modal {
position: fixed;
inset: 0;
z-index: 80;
display: grid;
place-items: center;
padding: 24px;
opacity: 0;
pointer-events: none;
transition: opacity .22s ease;
}
.logout-modal.show {
opacity: 1;
pointer-events: auto;
}
.logout-modal-backdrop {
position: absolute;
inset: 0;
background: rgba(32, 35, 34, .46);
backdrop-filter: blur(10px);
}
.logout-dialog {
position: relative;
z-index: 1;
width: min(420px, 100%);
padding: 34px;
border-radius: 8px;
background: #fff;
box-shadow: 0 28px 80px rgba(61, 73, 68, .24);
text-align: center;
transform: translateY(12px) scale(.98);
transition: transform .22s ease;
}
.logout-modal.show .logout-dialog {
transform: translateY(0) scale(1);
}
.logout-close {
position: absolute;
top: 14px;
right: 14px;
width: 34px;
height: 34px;
border: 0;
border-radius: 50%;
background: #f4f1eb;
color: #7f786f;
font-size: 24px;
line-height: 1;
cursor: pointer;
transition: transform .2s ease, background .2s ease, color .2s ease;
}
.logout-close:hover {
transform: rotate(90deg);
background: var(--red-soft);
color: var(--red);
}
.logout-icon {
display: inline-grid;
place-items: center;
width: 58px;
height: 58px;
border-radius: 50%;
margin-bottom: 16px;
background: var(--red-soft);
color: var(--red);
font-weight: 800;
}
.logout-dialog h2 {
margin: 0 0 10px;
font-size: 24px;
color: var(--ink);
}
.logout-dialog p {
margin: 0;
color: var(--muted);
line-height: 1.8;
}
.logout-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 26px;
}
.logout-actions .btn {
justify-content: center;
}
.logout-modal-open {
overflow: hidden;
}
@media (max-width: 1120px) {
.profile-hero-grid,
.dashboard-layout,
.profile-data-layout {
grid-template-columns: 1fr;
}
.dashboard-side {
position: static;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.content-grid,
.management-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 760px) {
.profile-hero {
padding-top: 36px;
}
.profile-card {
grid-template-columns: 1fr;
padding: 24px;
}
.profile-info h1 {
font-size: 36px;
}
.profile-stats,
.dashboard-side,
.family-grid,
.content-grid,
.management-grid,
.service-grid {
grid-template-columns: 1fr;
}
.family-card {
grid-template-columns: 88px 1fr;
}
.book-cover {
min-height: 120px;
font-size: 18px;
}
.content-tile.large {
grid-column: span 1;
}
.panel-head,
.card-head {
display: grid;
}
.data-list p {
grid-template-columns: 1fr;
gap: 4px;
}
.logout-dialog {
padding: 30px 22px;
}
.logout-actions {
grid-template-columns: 1fr;
}
}
+126
View File
@@ -0,0 +1,126 @@
.promo-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.promo-tags {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.promo-tags span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255, 253, 248, .74);
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.promo-tags span:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.album-preview {
display: grid;
grid-template-columns: 1.2fr 1fr;
grid-template-rows: repeat(2, 130px);
gap: 14px;
overflow: hidden;
}
.album-photo {
border-radius: 18px;
background: radial-gradient(circle at 72% 24%, rgba(255, 255, 255, .75) 0 8%, transparent 9%), linear-gradient(135deg, #e7c89e, #b94135 48%, #143d57);
box-shadow: 0 16px 36px rgba(57, 48, 36, .08);
transition: transform .26s ease;
}
.album-photo.large {
grid-row: 1 / 3;
background: linear-gradient(180deg, rgba(28, 48, 45, .1), rgba(21, 55, 51, .72)), url("../images/ancestral-hall.png") center/cover;
}
.album-photo.warm {
background: linear-gradient(135deg, #f0d8ac, #c49245 45%, #7d342d);
}
.album-photo.green {
background: linear-gradient(135deg, #dfd1b5, #6b7f70 48%, #26302c);
}
.album-preview:hover .album-photo {
transform: scale(1.025);
}
.album-card {
min-height: 180px;
position: relative;
overflow: hidden;
}
.album-card:after {
content: "";
position: absolute;
left: -42%;
top: 0;
width: 36%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .54), transparent);
transform: skewX(-18deg);
transition: left .56s ease;
}
.album-card:hover:after {
left: 112%;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@media (max-width: 720px) {
.hero-copy {
padding-left: 0;
}
.hero-copy:before {
display: none;
}
.album-preview {
grid-template-columns: 1fr;
grid-template-rows: repeat(4, 120px);
}
.album-photo.large {
grid-row: auto;
}
}
+140
View File
@@ -0,0 +1,140 @@
.promo-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.promo-tags {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.promo-tags span {
padding: 10px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255, 253, 248, .74);
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.promo-tags span:hover {
color: #fff;
background: var(--green);
transform: translateY(-3px);
}
.video-preview {
min-height: 300px;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: flex-end;
color: #fff;
background: linear-gradient(180deg, rgba(31, 63, 55, .1), rgba(25, 39, 34, .86)), url("../images/ancestral-hall.png") center/cover;
}
.video-preview:before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(rgba(255, 255, 255, .08) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, .08) 1px, transparent 1px);
background-size: 34px 34px;
opacity: .35;
}
.video-preview h3,
.video-preview p,
.play-button {
position: relative;
z-index: 2;
}
.video-preview p {
color: rgba(255, 255, 255, .8);
}
.play-button {
width: 64px;
height: 64px;
margin-bottom: 46px;
border-radius: 50%;
background: rgba(255, 255, 255, .94);
box-shadow: 0 14px 34px rgba(0, 0, 0, .2);
transition: transform .24s ease;
}
.play-button:after {
content: "";
position: absolute;
left: 26px;
top: 19px;
border-top: 13px solid transparent;
border-bottom: 13px solid transparent;
border-left: 20px solid var(--red);
}
.video-preview:hover .play-button {
transform: scale(1.08);
}
.video-card {
min-height: 180px;
position: relative;
overflow: hidden;
}
.video-card:after {
content: "";
position: absolute;
left: -42%;
top: 0;
width: 36%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .54), transparent);
transform: skewX(-18deg);
transition: left .56s ease;
}
.video-card:hover:after {
left: 112%;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@media (max-width: 720px) {
.hero-copy {
padding-left: 0;
}
.hero-copy:before {
display: none;
}
}
+900
View File
@@ -0,0 +1,900 @@
:root {
--red: #c83b32;
--red-soft: #f4ded8;
--green: #476f63;
--green-dark: #213f38;
--gold: #c49245;
--ink: #26302c;
--muted: #737b76;
--paper: #fbf7ed;
--paper-2: #f7efe2;
--line: #eadfce;
--white: #fffdf8;
--shadow: 0 24px 70px rgba(69, 53, 35, .12);
}
.auth-layer-message {
/* 认证页 layui 消息提示皮肤,替代浏览器原生 alert。 */
min-width: 160px;
border-radius: 999px !important;
background: rgba(38, 48, 44, .94) !important;
box-shadow: 0 16px 42px rgba(38, 48, 44, .24) !important;
}
.auth-layer-message .layui-layer-content {
padding: 10px 18px !important;
color: #fffdf8 !important;
font-size: 14px;
font-weight: 800;
line-height: 1.6;
text-align: center;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
color: var(--ink);
font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif;
background: var(--paper);
}
a {
color: inherit;
text-decoration: none;
}
img {
max-width: 100%;
display: block;
}
@media (hover: hover) and (pointer: fine) {
a,
.btn,
.card,
button,
summary {
cursor: pointer;
}
}
.container {
width: min(1280px, calc(100% - 72px));
margin: 0 auto;
}
.site-header {
position: sticky;
top: 0;
z-index: 20;
background:
linear-gradient(115deg, rgba(110, 38, 32, .96) 0%, rgba(200, 59, 50, .96) 48%, rgba(139, 52, 39, .96) 100%);
border-bottom: 1px solid rgba(255, 253, 248, .2);
box-shadow:
0 4px 0 rgba(93, 30, 26, .2),
0 18px 38px rgba(83, 32, 26, .34),
0 30px 70px rgba(83, 32, 26, .22);
backdrop-filter: blur(18px);
}
.nav {
height: 82px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 28px;
}
.brand {
display: flex !important;
align-items: center;
gap: 12px;
padding: 8px 14px;
border-radius: 999px;
background: rgba(255, 253, 248, .92);
box-shadow: 0 10px 28px rgba(63, 28, 22, .18);
font-size: 24px;
font-weight: 800;
white-space: nowrap;
}
.brand-logo {
display: block;
width: 270px;
height: auto;
}
.footer .brand-logo {
width: 270px;
}
.auth-page-brand .brand-logo {
width: 220px;
}
.brand-logo-mark {
width: 48px;
height: 48px;
}
.brand-text {
color: var(--ink);
font-weight: 900;
}
.brand-mark {
width: 40px;
height: 40px;
border-radius: 50%;
display: grid;
place-items: center;
color: #fff;
background: var(--red);
box-shadow: 0 10px 24px rgba(200, 59, 50, .22);
font-weight: 800;
}
.nav-links {
display: flex;
align-items: center;
gap: 26px;
color: rgba(255, 253, 248, .78);
font-size: 15px;
white-space: nowrap;
}
.nav-links a.active,
.nav-links a:hover {
color: #fff7d8;
}
.nav-links a,
.nav-actions a {
position: relative;
transition: color .22s ease, transform .22s ease;
}
.nav-links a:after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: -10px;
height: 2px;
border-radius: 999px;
background: linear-gradient(90deg, #f5d99b, #fff3c8);
transform: scaleX(0);
transform-origin: center;
transition: transform .24s ease;
}
.nav-links a.active:after,
.nav-links a:hover:after {
transform: scaleX(1);
}
.nav-actions {
display: flex;
align-items: center;
gap: 14px;
color: rgba(255, 253, 248, .86);
font-size: 15px;
white-space: nowrap;
}
.btn {
min-width: 118px;
height: 44px;
padding: 0 24px;
border-radius: 999px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid transparent;
font-weight: 800;
transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease, background .22s ease;
will-change: transform;
}
.btn:hover {
transform: translateY(-2px);
}
.btn.primary {
color: #fff;
background: var(--red);
box-shadow: 0 14px 30px rgba(200, 59, 50, .24);
}
.btn.primary:hover {
box-shadow: 0 18px 38px rgba(200, 59, 50, .3);
}
.site-header .btn.primary {
color: #8f332b;
background: linear-gradient(135deg, #fff8e6, #f1d49a);
box-shadow: 0 12px 28px rgba(64, 26, 21, .22);
}
.site-header .btn.primary:hover {
box-shadow: 0 16px 34px rgba(64, 26, 21, .3);
}
.btn.ghost {
color: var(--red);
border-color: rgba(200, 59, 50, .28);
background: rgba(255, 255, 255, .68);
}
.inner-hero {
position: relative;
overflow: hidden;
background:
radial-gradient(circle at 82% 12%, rgba(196, 146, 69, .16), transparent 24%),
linear-gradient(125deg, #fffaf0 0%, #f7efe2 48%, #edf4ef 100%);
}
.inner-hero:before {
content: "";
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(138, 50, 43, .05) 1px, transparent 1px),
linear-gradient(90deg, rgba(138, 50, 43, .05) 1px, transparent 1px);
background-size: 68px 68px;
mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
.inner-hero .container {
position: relative;
z-index: 2;
min-height: 410px;
padding: 68px 0 72px;
display: grid;
grid-template-columns: .9fr 1.1fr;
gap: 54px;
align-items: center;
}
.eyebrow {
display: inline-flex;
align-items: center;
gap: 12px;
margin-bottom: 22px;
color: #8a5a3b;
font-weight: 800;
}
.eyebrow:before {
content: "";
width: 42px;
height: 2px;
background: var(--red);
}
h1,
h2,
h3,
h4,
p {
margin-top: 0;
}
h1 {
margin-bottom: 20px;
color: var(--ink);
font-family: "SimSun", "Songti SC", serif;
font-size: 52px;
line-height: 1.16;
}
h2 {
margin-bottom: 14px;
font-family: "SimSun", "Songti SC", serif;
font-size: 42px;
line-height: 1.25;
}
h3 {
margin-bottom: 12px;
font-size: 23px;
}
.lead {
max-width: 620px;
margin-bottom: 0;
color: #645d52;
font-size: 18px;
line-height: 1.9;
}
.hero-card {
min-height: 260px;
padding: 28px;
border: 1px solid rgba(224, 211, 189, .88);
border-radius: 24px;
background: rgba(255, 253, 248, .92);
box-shadow: var(--shadow);
}
.section {
padding: 76px 0;
}
.section.alt {
background: #fffdf8;
}
.section-head {
margin-bottom: 34px;
text-align: center;
}
.section-head p {
max-width: 720px;
margin: 14px auto 0;
color: var(--muted);
font-size: 17px;
line-height: 1.9;
}
.grid-2,
.grid-3,
.grid-4 {
display: grid;
gap: 22px;
}
.grid-2 {
grid-template-columns: repeat(2, 1fr);
}
.grid-3 {
grid-template-columns: repeat(3, 1fr);
}
.grid-4 {
grid-template-columns: repeat(4, 1fr);
}
.card {
padding: 24px;
border-radius: 18px;
background: #fffdf8;
border: 1px solid var(--line);
box-shadow: 0 16px 42px rgba(57, 48, 36, .07);
transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease, background .24s ease;
will-change: transform;
}
.card:hover {
transform: translateY(-6px);
border-color: rgba(196, 146, 69, .38);
box-shadow: 0 24px 60px rgba(57, 48, 36, .11);
}
.card.soft {
background: #fbf7ed;
}
.card.dark {
color: #fff;
background: linear-gradient(155deg, var(--green-dark), var(--green));
}
.card.dark p {
color: rgba(255, 255, 255, .78);
}
.card p,
.muted {
color: var(--muted);
line-height: 1.8;
}
.tag-row {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.tag {
padding: 6px 12px;
border-radius: 999px;
color: var(--green);
background: #eaf0eb;
font-size: 14px;
font-weight: 700;
}
.toolbar {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.input,
select,
textarea {
width: 100%;
min-height: 48px;
padding: 0 16px;
border: 1px solid #e2d4bf;
border-radius: 999px;
color: var(--ink);
background: #fbf8f1;
font: inherit;
}
textarea {
min-height: 128px;
padding: 14px 16px;
border-radius: 18px;
resize: vertical;
}
.form-grid {
display: grid;
gap: 16px;
}
.form-card {
max-width: 560px;
margin: 0 auto;
padding: 34px;
}
.auth-layout {
min-height: calc(100vh - 82px);
display: grid;
place-items: center;
padding: 62px 24px;
background:
radial-gradient(circle at 18% 20%, rgba(200, 59, 50, .09), transparent 24%),
linear-gradient(125deg, #fffaf0, #f7efe2 54%, #edf4ef);
}
/* 认证类页面共用祖堂背景,登录、注册、找回密码只保留自己的差异样式 */
.auth-scenic-layout {
position: relative;
isolation: isolate;
min-height: 100vh;
padding: 56px 24px 72px;
place-items: center;
background:
linear-gradient(90deg, rgba(255, 253, 248, .86), rgba(255, 253, 248, .46) 45%, rgba(255, 253, 248, .88)),
linear-gradient(135deg, rgba(200, 59, 50, .08), transparent 34%),
url("../images/ancestral-hall-new.png") center / cover no-repeat;
}
.auth-scenic-layout:before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
background:
linear-gradient(180deg, rgba(251, 247, 237, .42), rgba(251, 247, 237, .86)),
radial-gradient(circle at 50% 42%, rgba(255, 253, 248, .3), transparent 30%);
backdrop-filter: blur(2px);
}
.auth-page-brand {
position: absolute;
left: clamp(18px, 4vw, 56px);
top: clamp(18px, 4vw, 42px);
z-index: 3;
}
/* 认证卡片公共样式,避免登录、注册、找回密码页面重复维护 */
.auth-card {
width: min(440px, 100%);
padding: 38px;
border-radius: 16px;
border-color: rgba(224, 211, 189, .9);
background: rgba(255, 253, 248, .94);
box-shadow: 0 26px 70px rgba(57, 48, 36, .16);
}
.auth-card:hover {
transform: none;
}
.auth-card h1 {
margin-bottom: 12px;
font-size: 40px;
}
.auth-card .lead {
margin-bottom: 28px;
font-size: 16px;
}
.auth-card .form-grid {
gap: 14px;
}
.auth-card .input {
height: 50px;
background: #fffaf1;
}
.auth-card .btn.primary,
.auth-card .btn.ghost {
position: relative;
overflow: hidden;
height: 50px;
}
.auth-card .btn.primary {
width: 100%;
margin-top: 2px;
}
.auth-card .btn.primary:before,
.auth-card .btn.ghost:before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: -45%;
width: 38%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .34), transparent);
transform: skewX(-18deg);
transition: left .48s ease;
}
.auth-card .btn.primary:hover,
.auth-card .btn.ghost:hover {
transform: translateY(-3px) scale(1.012);
}
.auth-card .btn.primary:hover {
box-shadow: 0 18px 42px rgba(200, 59, 50, .34);
}
.auth-card .btn.ghost:hover {
color: #fff;
border-color: rgba(200, 59, 50, .2);
background: var(--red);
box-shadow: 0 16px 34px rgba(200, 59, 50, .2);
}
.auth-card .btn.primary:hover:before,
.auth-card .btn.ghost:hover:before {
left: 110%;
}
.auth-card .btn.primary:active,
.auth-card .btn.ghost:active {
transform: translateY(-1px) scale(.99);
}
.auth-links {
display: flex;
justify-content: space-between;
gap: 16px;
color: #8a5a3b;
font-weight: 800;
}
.auth-links a {
transition: color .22s ease, transform .22s ease;
}
.auth-links a:hover {
color: var(--red);
transform: translateY(-1px);
}
.auth-captcha-box {
/* 认证页滑动验证挂载点,TAC 内部 DOM 由第三方库生成 */
display: flex;
justify-content: center;
}
.auth-captcha-box:empty {
display: none;
}
.auth-captcha-box #tianai-captcha-parent {
max-width: 100%;
}
.auth-captcha-modal {
/* 认证页滑动验证弹窗挂载点,TAC 内部 DOM 由第三方库生成 */
position: fixed;
inset: 0;
z-index: 1000;
display: grid;
place-items: center;
padding: 24px;
background: rgba(38, 48, 44, .32);
backdrop-filter: blur(4px);
}
.auth-captcha-modal:empty {
display: none;
}
.auth-captcha-modal #tianai-captcha-parent {
max-width: min(318px, calc(100vw - 32px));
max-height: calc(100vh - 32px);
}
/* 验证码输入和按钮的通用双列布局,窄屏在页面 CSS 中改为单列 */
.code-row {
display: grid;
grid-template-columns: 1fr auto;
gap: 12px;
}
/* 接口请求期间的公共状态,只由 JS 切换 class,不在 JS 内写样式 */
.is-loading {
pointer-events: none;
opacity: .72;
}
/* 接口列表为空时的统一占位样式 */
.api-empty {
padding: 22px;
border: 1px dashed var(--line);
border-radius: 14px;
color: var(--muted);
background: rgba(255, 253, 248, .7);
text-align: center;
font-weight: 800;
}
.footer {
padding: 60px 0 36px;
color: rgba(255, 255, 255, .78);
background: #3f3a35;
}
.footer-grid {
display: grid;
grid-template-columns: 1.2fr repeat(3, 1fr);
gap: 48px;
padding-bottom: 38px;
border-bottom: 1px solid rgba(255, 255, 255, .12);
}
.footer h4 {
margin: 0 0 18px;
color: #fff;
font-size: 17px;
}
.footer p,
.footer a {
display: block;
color: inherit;
margin: 10px 0;
line-height: 1.8;
}
.footer a {
width: fit-content;
transition: color .22s ease, transform .22s ease;
}
.footer a:hover {
color: #fff;
transform: translateX(4px);
}
.footer .brand {
padding: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.footer .brand:hover {
transform: translateX(0) translateY(-2px);
}
.copyright,
.footer-legal {
padding-top: 28px;
color: rgba(255, 255, 255, .48);
font-size: 14px;
}
.footer-legal {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 14px;
flex-wrap: wrap;
text-align: left;
}
.footer-legal a {
display: inline-flex;
width: auto;
margin: 0;
color: inherit;
font-size: inherit;
line-height: inherit;
}
.footer-legal a:hover {
color: #fff;
transform: none;
}
.profile-layer-confirm {
border-radius: 8px !important;
overflow: hidden;
}
.profile-layer-confirm .layui-layer-title {
height: 50px;
line-height: 50px;
border-bottom: 1px solid rgba(224, 211, 189, .72);
background: #fffaf0;
color: var(--ink);
font-weight: 900;
}
.profile-layer-confirm .layui-layer-content {
color: var(--muted);
line-height: 1.8;
}
.profile-layer-confirm .layui-layer-btn a {
border-radius: 999px;
}
.profile-layer-confirm .layui-layer-btn .layui-layer-btn0 {
border-color: var(--red);
background: var(--red);
}
.pending-layer-dialog {
border-radius: 8px !important;
overflow: hidden;
box-shadow: 0 20px 60px rgba(38, 30, 21, .18) !important;
}
.pending-layer-dialog .layui-layer-title {
height: 52px;
line-height: 52px;
border-bottom: 1px solid rgba(224, 211, 189, .72);
background: #fffaf0;
color: var(--ink);
font-weight: 900;
font-size: 16px;
}
.pending-layer-dialog .layui-layer-content {
padding: 28px 28px 16px !important;
color: var(--ink);
font-size: 16px;
line-height: 1.8;
text-align: center;
}
.pending-layer-dialog .layui-layer-btn {
padding: 0 24px 24px;
text-align: center;
}
.pending-layer-dialog .layui-layer-btn a {
height: 38px;
min-width: 108px;
padding: 0 24px;
border-radius: 999px;
line-height: 38px;
font-weight: 800;
}
.pending-layer-dialog .layui-layer-btn .layui-layer-btn0 {
border-color: var(--red);
background: var(--red);
}
.profile-popup-select {
display: grid;
gap: 10px;
padding: 4px 0;
}
.profile-popup-select button {
width: 100%;
min-height: 42px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fffdf8;
color: var(--ink);
font: inherit;
font-weight: 900;
cursor: pointer;
transition: border-color .2s ease, color .2s ease, background .2s ease;
}
.profile-popup-select button:hover {
border-color: rgba(200, 59, 50, .38);
color: var(--red);
background: #fff4ef;
}
@media (max-width: 1120px) {
.nav-links {
display: none;
}
.inner-hero .container,
.grid-2 {
grid-template-columns: 1fr;
}
.grid-3,
.grid-4 {
grid-template-columns: repeat(2, 1fr);
}
.footer-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 720px) {
.container {
width: calc(100% - 28px);
}
.nav-actions a:not(.btn) {
display: none;
}
.inner-hero .container {
padding: 46px 0 54px;
}
h1 {
font-size: 38px;
}
h2 {
font-size: 32px;
}
.grid-3,
.grid-4,
.footer-grid {
grid-template-columns: 1fr;
}
.auth-scenic-layout {
padding: 36px 14px 48px;
align-items: start;
}
.auth-card {
padding: 28px 22px;
}
.auth-card h1 {
font-size: 34px;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*:before,
*:after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
transition-duration: .01ms !important;
}
}
+10
View File
@@ -0,0 +1,10 @@
/* 注册页只保留单页差异,公共认证卡片样式在 public.css */
.page-register .auth-links {
justify-content: center;
}
@media (max-width: 720px) {
.page-register .auth-layout {
align-items: start;
}
}
+119
View File
@@ -0,0 +1,119 @@
.result-hero .container {
min-height: 420px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.result-search {
display: grid;
gap: 18px;
align-content: center;
}
.result-search .toolbar {
padding: 8px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
background: rgba(255, 250, 241, .82);
}
.result-search .input {
flex: 1 1 260px;
border: 0;
background: transparent;
}
.result-list {
display: grid;
gap: 18px;
max-width: 920px;
margin: 0 auto;
}
.result-card {
position: relative;
overflow: hidden;
padding: 26px;
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255, 253, 248, .82);
box-shadow: 0 18px 48px rgba(57, 48, 36, .07);
transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease;
}
.result-card:after {
content: "";
position: absolute;
right: -70px;
bottom: -80px;
width: 180px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .13), transparent 68%);
transition: transform .28s ease;
}
.result-card:hover {
border-color: rgba(196, 146, 69, .42);
box-shadow: 0 24px 60px rgba(57, 48, 36, .11);
}
.result-card:hover:after {
transform: scale(1.2);
}
.result-card span {
display: inline-flex;
margin-bottom: 12px;
padding: 7px 12px;
border-radius: 999px;
color: var(--green-dark);
background: #e8f0eb;
font-weight: 800;
font-size: 13px;
}
.result-card p {
margin: 0;
color: var(--muted);
line-height: 1.8;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@media (max-width: 720px) {
.hero-copy {
padding-left: 0;
}
.hero-copy:before {
display: none;
}
.result-search .toolbar {
border-radius: 18px;
}
}
+111
View File
@@ -0,0 +1,111 @@
.ticket-hero .container {
min-height: 420px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.ticket-tip {
display: grid;
place-items: center;
text-align: center;
background: linear-gradient(145deg, var(--green-dark), var(--green));
color: #fff;
}
.ticket-tip span {
width: 88px;
height: 88px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--red);
background: rgba(255, 253, 248, .94);
font-size: 28px;
font-weight: 900;
}
.ticket-tip p {
max-width: 360px;
margin: 18px 0 0;
color: rgba(255, 255, 255, .86);
line-height: 1.8;
}
.ticket-layout {
display: grid;
grid-template-columns: minmax(0, 720px) 320px;
gap: 26px;
align-items: start;
}
.ticket-form {
max-width: none;
}
.ticket-form textarea {
min-height: 150px;
padding-top: 16px;
border-radius: 18px;
resize: vertical;
}
.ticket-side {
padding: 26px;
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255, 253, 248, .82);
box-shadow: 0 20px 54px rgba(57, 48, 36, .08);
}
.ticket-side p {
color: var(--muted);
line-height: 1.8;
}
.ticket-side a {
display: inline-flex;
margin-top: 10px;
color: var(--red);
font-weight: 800;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@media (max-width: 900px) {
.ticket-layout {
grid-template-columns: 1fr;
}
}
@media (max-width: 720px) {
.hero-copy {
padding-left: 0;
}
.hero-copy:before {
display: none;
}
}
+169
View File
@@ -0,0 +1,169 @@
.surname-detail-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.detail-actions {
display: flex;
gap: 14px;
margin-top: 30px;
flex-wrap: wrap;
}
.surname-emblem {
position: relative;
overflow: hidden;
display: grid;
place-items: center;
text-align: center;
color: #fff;
background:
radial-gradient(circle at 72% 24%, rgba(255, 255, 255, .28) 0 10%, transparent 11%),
linear-gradient(135deg, #476f63, #c49245 54%, #8f4634);
}
.surname-emblem span {
width: 112px;
height: 112px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--red);
background: rgba(255, 253, 248, .95);
font-family: "SimSun", "Songti SC", serif;
font-size: 58px;
font-weight: 800;
transition: transform .24s ease;
}
.surname-emblem:hover span {
transform: rotate(-8deg) scale(1.08);
}
.surname-emblem p {
margin: 18px 0 0;
color: rgba(255, 255, 255, .9);
font-size: 22px;
}
.surname-detail-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
.content-card,
.related-panel {
border: 1px solid var(--line);
border-radius: 18px;
background: rgba(255, 253, 248, .82);
box-shadow: 0 20px 54px rgba(57, 48, 36, .08);
}
.content-card {
padding: 30px;
}
.content-card:first-child {
grid-column: 1 / -1;
}
.content-card p {
color: var(--muted);
font-size: 17px;
line-height: 1.9;
}
.generation-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.generation-row span {
width: 54px;
height: 54px;
border-radius: 14px;
display: grid;
place-items: center;
color: #7c4a33;
background: #fbf7ed;
border: 1px solid var(--line);
font-family: "SimSun", "Songti SC", serif;
font-size: 24px;
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.generation-row span:hover {
color: #fff;
background: var(--red);
transform: translateY(-5px);
}
.related-panel {
padding: 26px;
}
.related-panel a {
display: block;
margin-top: 12px;
padding: 14px 16px;
border: 1px solid var(--line);
border-radius: 14px;
color: #6f4a37;
background: #fbf7ed;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.related-panel a:hover {
color: #fff;
background: var(--green);
transform: translateX(5px);
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@media (max-width: 860px) {
.surname-detail-grid {
grid-template-columns: 1fr;
}
.content-card:first-child {
grid-column: auto;
}
}
@media (max-width: 720px) {
.hero-copy {
padding-left: 0;
}
.hero-copy:before {
display: none;
}
}
+229
View File
@@ -0,0 +1,229 @@
.surname-hero .container {
min-height: 440px;
padding-top: 74px;
padding-bottom: 74px;
}
.hero-copy {
position: relative;
padding-left: 28px;
}
.hero-copy:before {
content: "";
position: absolute;
left: 0;
top: 58px;
width: 4px;
height: 132px;
border-radius: 999px;
background: linear-gradient(180deg, var(--red), var(--gold));
}
.surname-stats {
display: flex;
gap: 12px;
margin-top: 28px;
flex-wrap: wrap;
}
.surname-stats span {
padding: 11px 14px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
color: #6d766f;
background: rgba(255, 253, 248, .74);
box-shadow: 0 14px 32px rgba(72, 57, 38, .06);
}
.surname-stats b {
margin-right: 6px;
color: var(--green);
}
.surname-search-card {
position: relative;
overflow: hidden;
display: grid;
gap: 18px;
align-content: center;
}
.search-ornament {
position: absolute;
right: -72px;
top: -84px;
width: 220px;
height: 220px;
border-radius: 50%;
background: radial-gradient(circle, rgba(200, 59, 50, .13), transparent 62%);
animation: surnamePulse 4s ease-in-out infinite;
}
.surname-search-card h3,
.search-toolbar,
.hot-query {
position: relative;
z-index: 2;
}
.search-toolbar {
padding: 8px;
border: 1px solid rgba(224, 211, 189, .78);
border-radius: 999px;
background: rgba(255, 250, 241, .82);
}
.search-toolbar .input {
flex: 1 1 280px;
border: 0;
background: transparent;
}
.hot-query {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.hot-query span {
width: 38px;
height: 38px;
border-radius: 12px;
display: grid;
place-items: center;
color: #7c4a33;
background: #fbf7ed;
border: 1px solid var(--line);
font-family: "SimSun", "Songti SC", serif;
font-weight: 800;
transition: transform .22s ease, color .22s ease, background .22s ease;
}
.hot-query span:hover {
color: #fff;
background: var(--red);
transform: translateY(-4px);
}
.surname-list-section {
position: relative;
overflow: hidden;
}
.surname-list-section:before {
content: "";
position: absolute;
left: 50%;
top: 58px;
width: 520px;
height: 180px;
border-radius: 50%;
background: radial-gradient(circle, rgba(196, 146, 69, .09), transparent 68%);
transform: translateX(-50%);
}
.surname-grid {
position: relative;
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 12px;
padding: 24px;
border: 1px solid rgba(224, 211, 189, .8);
border-radius: 24px;
background: rgba(255, 253, 248, .72);
box-shadow: 0 22px 60px rgba(57, 48, 36, .08);
}
.surname-grid a {
height: 64px;
border-radius: 14px;
display: grid;
place-items: center;
color: #7c4a33;
background: #fbf7ed;
border: 1px solid var(--line);
font-family: "SimSun", "Songti SC", serif;
font-size: 24px;
font-weight: 800;
transition: transform .24s ease, color .24s ease, background .24s ease, border-color .24s ease, box-shadow .24s ease;
will-change: transform;
}
.surname-grid a:hover {
color: #fff;
background: var(--red);
border-color: rgba(200, 59, 50, .72);
box-shadow: 0 16px 34px rgba(200, 59, 50, .18);
transform: translateY(-6px) scale(1.03);
}
.surname-grid:hover a {
animation: surnameFloat 1.9s ease-in-out infinite;
}
.surname-grid:hover a:nth-child(2n) {
animation-delay: .08s;
}
.surname-grid:hover a:nth-child(3n) {
animation-delay: .14s;
}
.surname-grid:hover a:nth-child(4n) {
animation-delay: .2s;
}
.tilt-card {
transform: perspective(900px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)) translateY(var(--lift, 0));
transition: transform .18s ease, box-shadow .24s ease, border-color .24s ease;
}
.tilt-card:hover {
--lift: -6px;
}
@keyframes surnameFloat {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-4px);
}
}
@keyframes surnamePulse {
0%,
100% {
opacity: .5;
transform: scale(.92);
}
50% {
opacity: 1;
transform: scale(1.08);
}
}
@media (max-width: 720px) {
.hero-copy {
padding-left: 0;
}
.hero-copy:before {
display: none;
}
.search-toolbar {
border-radius: 18px;
}
.surname-grid {
grid-template-columns: repeat(4, 1fr);
padding: 14px;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

+314
View File
@@ -0,0 +1,314 @@
(function (root, factory) {
// 相册模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.AlbumPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.AlbumPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
var selectedAlbumId = '';
function normalizeList(data) {
// 相册接口列表响应兼容数组、rows、records、list、data。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 相册名称和照片说明进入 innerHTML 前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
var page = query('[data-album-page], [data-promo-album-page]');
return getQueryParam(search, 'genealogyId') ||
(page && page.getAttribute('data-genealogy-id')) ||
getQueryParam(search, 'id') ||
'';
}
function trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function toNumber(value, fallback) {
var text = String(value === undefined || value === null ? '' : value).trim();
var number = Number(text);
return text && Number.isFinite(number) ? number : fallback;
}
function buildAlbumBody(values) {
// AlbumBody 来自 APP.openapi.jsonalbumName 必填。
var source = values || {};
return {
albumName: String(source.albumName || '').trim(),
albumDesc: trimOrUndefined(source.albumDesc),
coverOssId: toNumber(source.coverOssId),
sortOrder: toNumber(source.sortOrder, 1),
status: String(source.status || '').trim() || '0'
};
}
function buildPhotoBody(values) {
// AlbumPhotoBody 来自 APP.openapi.jsonossId 必填。
var source = values || {};
return {
ossId: toNumber(source.ossId),
photoTitle: trimOrUndefined(source.photoTitle),
photoDesc: trimOrUndefined(source.photoDesc),
photographer: trimOrUndefined(source.photographer),
shootTime: trimOrUndefined(source.shootTime),
sortOrder: toNumber(source.sortOrder, 1),
status: String(source.status || '').trim() || '0'
};
}
function getAlbumId(item) {
return pick(item, ['albumId', 'id'], '');
}
function buildAlbumRow(item) {
var albumId = getAlbumId(item);
var name = pick(item, ['albumName', 'name'], '未命名相册');
var photoCount = pick(item, ['photoCount', 'photos'], '0');
var desc = pick(item, ['albumDesc', 'description'], '暂无说明');
return '<button class="module-row album-row" type="button" data-album-id="' + escapeHtml(albumId) + '">' +
'<div><h3>' + escapeHtml(name) + '</h3><p>' + escapeHtml(photoCount) + ' 张照片 · ' + escapeHtml(desc) + '</p></div>' +
'<span class="pill">管理</span></button>';
}
function buildPhotoRow(item) {
var title = pick(item, ['photoTitle', 'title'], '未命名照片');
var desc = pick(item, ['photoDesc', 'description'], '暂无说明');
var photographer = pick(item, ['photographer'], '未记录拍摄人');
return '<div class="module-row album-photo-row"><div><h3>' + escapeHtml(title) + '</h3><p>' + escapeHtml(desc) + ' · ' + escapeHtml(photographer) + '</p></div><span class="pill">照片</span></div>';
}
function renderAlbums(items) {
var container = query('[data-album-list], [data-promo-album-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无相册</div>';
return;
}
container.innerHTML = list.map(buildAlbumRow).join('');
}
function renderPhotos(items) {
var container = query('[data-album-photo-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无照片</div>';
return;
}
container.innerHTML = list.map(buildPhotoRow).join('');
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
values[field.name] = field.value;
});
return values;
}
async function loadAlbums() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
if (!api || !genealogyId) return;
try {
renderAlbums(await api.albums(genealogyId));
} catch (error) {
showMessage(error.message || '相册加载失败');
}
}
async function loadPhotos(albumId) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
if (!api || !genealogyId || !albumId) return;
selectedAlbumId = albumId;
try {
renderPhotos(await api.albumPhotos(genealogyId, albumId));
} catch (error) {
showMessage(error.message || '照片加载失败');
}
}
async function submitAlbum(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var body = buildAlbumBody(getFormValues(form));
if (!api || !genealogyId) {
showMessage('请从具体家谱进入相册管理');
return;
}
if (!body.albumName) {
showMessage('请填写相册名称');
return;
}
try {
if (selectedAlbumId) {
await api.updateAlbum(genealogyId, selectedAlbumId, body);
} else {
await api.createAlbum(genealogyId, body);
}
form.reset();
selectedAlbumId = '';
showMessage('相册已保存');
loadAlbums();
} catch (error) {
showMessage(error.message || '保存相册失败');
}
}
async function submitPhoto(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var body = buildPhotoBody(getFormValues(form));
if (!api || !genealogyId || !selectedAlbumId) {
showMessage('请先选择相册');
return;
}
if (!body.ossId) {
showMessage('请填写照片 OSS ID');
return;
}
try {
await api.createAlbumPhoto(genealogyId, selectedAlbumId, body);
form.reset();
showMessage('照片已添加');
loadPhotos(selectedAlbumId);
} catch (error) {
showMessage(error.message || '添加照片失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('click', function (event) {
var albumButton = event.target.closest('[data-album-id]');
if (!albumButton) return;
event.preventDefault();
loadPhotos(albumButton.getAttribute('data-album-id'));
});
documentRef.addEventListener('submit', function (event) {
var albumForm = event.target.closest('[data-album-form]');
var photoForm = event.target.closest('[data-album-photo-form]');
if (albumForm) {
event.preventDefault();
submitAlbum(albumForm);
}
if (photoForm) {
event.preventDefault();
submitPhoto(photoForm);
}
});
}
function init() {
if (!documentRef) return;
bindActions();
loadAlbums();
}
return {
normalizeList: normalizeList,
buildAlbumBody: buildAlbumBody,
buildPhotoBody: buildPhotoBody,
buildAlbumRow: buildAlbumRow,
init: init
};
});
+449
View File
@@ -0,0 +1,449 @@
(function (root, factory) {
// 同一份接口客户端同时支持浏览器页面和 Node 测试环境。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.GenealogyApi = factory(root);
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var DEFAULT_BASE_URL = 'http://test-genealogy-api.ddxcjp.cn';
var DEFAULT_CLIENT_ID = 'ced7e5f0498645c6ec642dcf450b036f';
var DEFAULT_TENANT_ID = '000000';
var DEFAULT_TOKEN_KEY = 'genealogy_auth_token';
var DEFAULT_TOKEN_HEADER_NAME = 'Authorization';
function loadNodeModule(path) {
if (typeof require !== 'function') return null;
try {
return require(path);
} catch (error) {
return null;
}
}
function getConfigApi() {
var configFactory;
if (root.GenealogyConfig) return root.GenealogyConfig;
configFactory = loadNodeModule('../../config.js');
return configFactory ? configFactory(root) : null;
}
function getStorageUtil() {
return root.StorageUtil || loadNodeModule('../../utils/StorageUtil.js');
}
function getRequestUtil() {
return root.RequestUtil || loadNodeModule('../../utils/RequestUtil.js');
}
function getStorage(store) {
if (store) return store;
try {
return root.localStorage;
} catch (error) {
return null;
}
}
function getTokenFromResponse(data) {
// 登录响应可能使用不同 token 字段,这里统一收敛为本地登录票据。
if (!data) return '';
return data.token || data.accessToken || data.tokenValue || '';
}
function ApiError(message, options) {
this.name = 'ApiError';
this.message = message || '接口请求失败';
this.status = options && options.status;
this.code = options && options.code;
this.data = options && options.data;
}
ApiError.prototype = Object.create(Error.prototype);
ApiError.prototype.constructor = ApiError;
function createUnavailableMethod(name) {
return function () {
var error = new Error('PC 接口文档未提供该业务接口:' + name);
error.code = 'PC_API_NOT_AVAILABLE';
throw error;
};
}
function createFormData(fileOrFormData) {
var FormDataCtor = root.FormData;
var formData;
if (FormDataCtor && fileOrFormData instanceof FormDataCtor) return fileOrFormData;
if (!FormDataCtor) throw new ApiError('当前环境不支持文件上传');
formData = new FormDataCtor();
formData.append('file', fileOrFormData);
return formData;
}
function createChunkFormData(body) {
var FormDataCtor = root.FormData;
var source = body || {};
var formData;
if (FormDataCtor && body instanceof FormDataCtor) return body;
if (!FormDataCtor) throw new ApiError('当前环境不支持文件上传');
formData = new FormDataCtor();
formData.append('uploadId', source.uploadId);
formData.append('chunkIndex', source.chunkIndex);
formData.append('chunkMd5', source.chunkMd5);
formData.append('file', source.file);
return formData;
}
function createClient(options) {
var settings = options || {};
var configApi = getConfigApi();
var config = configApi && configApi.getConfig ? configApi.getConfig() : {};
var storageUtil = getStorageUtil();
var requestUtil = getRequestUtil();
var store = getStorage(settings.tokenStore || settings.storage);
var clientId = settings.clientId || config.clientId || DEFAULT_CLIENT_ID;
var tenantId = settings.tenantId || config.tenantId || DEFAULT_TENANT_ID;
var tokenKey = settings.tokenKey || config.tokenKey || DEFAULT_TOKEN_KEY;
var tokenHeaderName = settings.tokenHeaderName || config.tokenHeaderName || DEFAULT_TOKEN_HEADER_NAME;
var baseUrl = settings.baseUrl || config.apiBaseUrl || DEFAULT_BASE_URL;
var requester;
if (!requestUtil || !requestUtil.createRequester) {
throw new ApiError('缺少 RequestUtil.createRequester 公共请求工具');
}
function getToken() {
return storageUtil && storageUtil.read ? storageUtil.read(store, tokenKey) || '' : '';
}
function setToken(token) {
if (token && storageUtil && storageUtil.write) storageUtil.write(store, tokenKey, token);
}
function clearToken() {
if (storageUtil && storageUtil.remove) storageUtil.remove(store, tokenKey);
}
requester = requestUtil.createRequester({
baseUrl: baseUrl,
clientId: clientId,
tokenHeaderName: tokenHeaderName,
getToken: getToken,
fetchImpl: settings.fetchImpl || root.fetch
});
function request(method, path, requestOptions) {
return requester(method, path, requestOptions || {});
}
function withTenant(body) {
return Object.assign({
tenantId: tenantId,
clientId: clientId
}, body || {});
}
async function login(body) {
var data = await request('POST', '/genealogy/pc/auth/login', {
auth: false,
body: Object.assign({
grantType: 'password'
}, withTenant(body))
});
setToken(getTokenFromResponse(data));
return data;
}
async function register(body) {
var data = await request('POST', '/genealogy/pc/auth/register', {
auth: false,
body: Object.assign({
grantType: 'password',
registerSource: 'PC'
}, withTenant(body))
});
setToken(getTokenFromResponse(data));
return data;
}
async function loginBySms(body) {
var data = await request('POST', '/genealogy/pc/auth/login/sms', {
auth: false,
body: Object.assign({
grantType: 'sms'
}, withTenant(body))
});
setToken(getTokenFromResponse(data));
return data;
}
function sendSmsCode(body) {
return request('POST', '/genealogy/pc/auth/sms/code', {
auth: false,
body: Object.assign({
grantType: 'sms'
}, withTenant(body))
});
}
function captchaRequire(body) {
var source = body || {};
return request('GET', '/captcha/require', {
auth: false,
query: {
tenantId: source.tenantId || tenantId,
clientId: source.clientId || clientId,
sceneCode: source.sceneCode,
subject: source.subject
}
});
}
function buildApiUrl(path, query) {
if (requestUtil.joinUrl && requestUtil.appendQuery) {
return requestUtil.appendQuery(requestUtil.joinUrl(baseUrl, path), query);
}
return baseUrl + path;
}
var client = {
clientId: clientId,
tenantId: tenantId,
tokenKey: tokenKey,
getToken: getToken,
setToken: setToken,
clearToken: clearToken,
request: request,
buildApiUrl: buildApiUrl,
get: function (path, query) {
return request('GET', path, { query: query });
},
post: function (path, body, reqOptions) {
return request('POST', path, Object.assign({}, reqOptions || {}, { body: body }));
},
put: function (path, body, reqOptions) {
return request('PUT', path, Object.assign({}, reqOptions || {}, { body: body }));
},
delete: function (path, reqOptions) {
return request('DELETE', path, reqOptions || {});
},
login: login,
register: register,
sendSmsCode: sendSmsCode,
loginBySms: loginBySms,
smsLogin: loginBySms,
getProfile: function () {
return request('GET', '/genealogy/pc/auth/profile');
},
currentProfile: function () {
return request('GET', '/genealogy/pc/auth/profile');
},
updateProfile: function (body) {
return request('PUT', '/genealogy/pc/auth/profile', { body: body });
},
changePassword: function (body) {
return request('PUT', '/genealogy/pc/auth/password', { body: body });
},
resetPassword: function (body) {
return request('PUT', '/genealogy/pc/auth/password/reset', {
auth: false,
body: Object.assign({
tenantId: tenantId
}, body || {})
});
},
changePhone: function (body) {
return request('PUT', '/genealogy/pc/auth/phone', { body: body });
},
deactivateAccount: async function (body) {
var data = await request('POST', '/genealogy/pc/auth/account/deactivate', { body: body });
clearToken();
return data;
},
logout: async function () {
var data = await request('DELETE', '/genealogy/pc/auth/logout');
clearToken();
return data;
},
uploadFile: function (fileOrFormData) {
return request('POST', '/genealogy/pc/files/upload', {
body: createFormData(fileOrFormData)
});
},
initResumableUpload: function (body) {
return request('POST', '/genealogy/pc/files/resumable/init', { body: body });
},
uploadChunk: function (body) {
return request('POST', '/genealogy/pc/files/resumable/chunk', {
body: createChunkFormData(body)
});
},
uploadResumableChunk: function (body) {
return this.uploadChunk(body);
},
completeResumableUpload: function (body) {
return request('POST', '/genealogy/pc/files/resumable/complete', { body: body });
},
bindFileReference: function (body) {
return request('POST', '/genealogy/pc/files/reference', { body: body });
},
releaseFileReference: function (query) {
return request('DELETE', '/genealogy/pc/files/reference', { query: query });
},
getRegionChildren: function (parentCode) {
return request('GET', '/genealogy/region/children', {
query: {
parentCode: parentCode
}
});
},
regionChildren: function (parentCode) {
return this.getRegionChildren(parentCode);
},
getRegionPath: function (regionCode) {
return request('GET', '/genealogy/region/path/' + encodeURIComponent(regionCode));
},
regionPath: function (regionCode) {
return this.getRegionPath(regionCode);
},
searchRegions: function (query) {
return request('GET', '/genealogy/region/search', { query: query });
},
regionSearch: function (query) {
return this.searchRegions(query);
},
getRegion: function (regionCode) {
return request('GET', '/genealogy/region/' + encodeURIComponent(regionCode));
},
regionDetail: function (regionCode) {
return this.getRegion(regionCode);
},
captchaRequire: captchaRequire,
captchaRequirement: captchaRequire,
captchaChallenge: function (body) {
return request('POST', '/captcha/challenge', { auth: false, body: body });
},
captchaVerify: function (body) {
return request('POST', '/captcha/verify', { auth: false, body: body });
},
legacyCaptcha: function () {
return request('GET', '/auth/code', { auth: false });
}
};
// PC YAML 未提供这些业务 paths,保留方法名只用于阻止旧移动端路径继续发请求。
[
'listGenealogies',
'createGenealogy',
'publicGenealogies',
'myGenealogies',
'genealogyDetail',
'genealogyOverview',
'applyJoinGenealogy',
'myJoinApplies',
'pendingJoinApplies',
'auditJoinApply',
'cancelJoinApply',
'genealogyMembers',
'genealogyMemberOptions',
'updateGenealogyMember',
'removeGenealogyMember',
'leaveGenealogy',
'transferGenealogyOwner',
'articleCategories',
'articles',
'articleDetail',
'createArticle',
'updateArticle',
'feeds',
'feedsPage',
'feedDetail',
'createFeed',
'updateFeed',
'likeFeed',
'unlikeFeed',
'feedComments',
'feedCommentsPage',
'createFeedComment',
'deleteFeedComment',
'albums',
'createAlbum',
'updateAlbum',
'albumPhotos',
'createAlbumPhoto',
'ceremonies',
'ceremonyDetail',
'createCeremony',
'updateCeremony',
'ceremonyGifts',
'createCeremonyGift',
'meritRecords',
'createMeritRecord',
'growthRecords',
'growthRecordDetail',
'createGrowthRecord',
'updateGrowthRecord',
'memos',
'memoDetail',
'createMemo',
'updateMemo',
'vipPackages',
'vipOrders',
'createVipOrder',
'generationPoems',
'createGenerationPoem',
'updateGenerationPoem',
'previewGenerationPoems',
'saveGenerationPoemsBatch',
'lineageTree',
'lineagePersons',
'lineagePersonsPage',
'lineagePersonOptions',
'lineagePersonDetail',
'createLineagePerson',
'updateLineagePerson',
'deleteLineagePerson',
'addLineageRelation',
'notifications',
'markNotificationRead',
'markAllNotificationsRead',
'helpArticles',
'helpArticleDetail',
'promotions',
'feedbackList',
'submitFeedback'
].forEach(function (name) {
client[name] = createUnavailableMethod(name);
});
return client;
}
return {
DEFAULT_BASE_URL: DEFAULT_BASE_URL,
DEFAULT_CLIENT_ID: DEFAULT_CLIENT_ID,
DEFAULT_TENANT_ID: DEFAULT_TENANT_ID,
TOKEN_KEY: DEFAULT_TOKEN_KEY,
ApiError: ApiError,
createClient: createClient,
defaultClient: createClient()
};
});
+297
View File
@@ -0,0 +1,297 @@
(function (root, factory) {
// 谱文模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.ArticlePages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.ArticlePages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 通用列表响应兼容数组、rows、records、list、data。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 标题和摘要进入 innerHTML 前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
var page = query('[data-article-page], [data-article-edit-page], [data-article-detail-page], [data-content-page]');
return getQueryParam(search, 'genealogyId') ||
getQueryParam(search, 'familyId') ||
(page && page.getAttribute('data-genealogy-id')) ||
getQueryParam(search, 'id') ||
'';
}
function getCurrentArticleId() {
var search = root.location && root.location.search;
return getQueryParam(search, 'articleId') || getQueryParam(search, 'id') || '';
}
function trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function toNumber(value, fallback) {
var text = String(value === undefined || value === null ? '' : value).trim();
var number = Number(text);
return text && Number.isFinite(number) ? number : fallback;
}
function formatStatus(status) {
var value = String(status === undefined || status === null ? '' : status);
if (value === '0' || value === 'published') return '已发布';
if (value === '1' || value === 'draft') return '草稿';
return value || '未知';
}
function buildArticleBody(values) {
// ArticleBody 来自 APP.openapi.json,标题和正文为核心必填。
var source = values || {};
return {
categoryId: toNumber(source.categoryId),
articleTitle: String(source.articleTitle || '').trim(),
articleSummary: trimOrUndefined(source.articleSummary),
coverOssId: toNumber(source.coverOssId),
articleContent: String(source.articleContent || '').trim(),
authorName: trimOrUndefined(source.authorName),
sortOrder: toNumber(source.sortOrder, 1),
status: String(source.status || '').trim() || '0'
};
}
function getArticleId(item) {
return pick(item, ['articleId', 'id'], '');
}
function buildArticleRow(item, genealogyId) {
var articleId = getArticleId(item);
var title = pick(item, ['articleTitle', 'title'], '未命名谱文');
var category = pick(item, ['categoryName', 'categoryTitle', 'typeName'], '谱文');
var status = formatStatus(pick(item, ['status'], '0'));
var updateTime = pick(item, ['updateTime', 'publishTime', 'createTime'], '未记录时间');
return '<a class="module-row article-row" href="article-detail.html?id=' + encodeURIComponent(articleId) + '&genealogyId=' + encodeURIComponent(genealogyId || '') + '">' +
'<div><h3>' + escapeHtml(title) + '</h3><p>' + escapeHtml(category) + ' · ' + escapeHtml(status) + ' · ' + escapeHtml(updateTime) + '</p></div>' +
'<span class="pill">查看</span></a>';
}
function buildCategoryOption(item) {
var categoryId = pick(item, ['categoryId', 'id'], '');
var name = pick(item, ['categoryName', 'name', 'title'], '未命名分类');
return '<option value="' + escapeHtml(categoryId) + '">' + escapeHtml(name) + '</option>';
}
function renderArticles(items, genealogyId) {
var container = query('[data-article-list], [data-recent-content]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无谱文内容</div>';
return;
}
container.innerHTML = list.map(function (item) {
return buildArticleRow(item, genealogyId);
}).join('');
}
function renderCategories(items) {
var select = query('[data-article-category]');
var list = normalizeList(items);
if (!select) return;
select.innerHTML = '<option value="">请选择分类</option>' + list.map(buildCategoryOption).join('');
}
function renderArticleDetail(article) {
var source = article || {};
var title = query('[data-article-title]');
var summary = query('[data-article-summary]');
var meta = query('[data-article-meta]');
var content = query('[data-article-content]');
if (title) title.textContent = pick(source, ['articleTitle', 'title'], '未命名谱文');
if (summary) summary.textContent = pick(source, ['articleSummary', 'summary'], '暂无摘要');
if (meta) {
meta.innerHTML = '<span>' + escapeHtml(pick(source, ['categoryName'], '谱文')) + '</span><span>' + escapeHtml(pick(source, ['authorName'], '佚名')) + '</span><span>' + escapeHtml(pick(source, ['updateTime', 'publishTime', 'createTime'], '未记录时间')) + '</span>';
}
if (content) content.innerHTML = pick(source, ['articleContent', 'content'], '<p>暂无正文</p>');
}
function fillArticleForm(article) {
var source = article || {};
queryAll('[name]').forEach(function (field) {
var value = pick(source, [field.name], '');
if (value !== '') field.value = value;
});
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
values[field.name] = field.value;
});
return values;
}
async function loadArticles() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var articleId = getCurrentArticleId();
if (!api || !genealogyId) return;
try {
if (query('[data-article-list], [data-recent-content]')) {
renderArticles(await api.articles(genealogyId), genealogyId);
}
if (query('[data-article-category]')) {
renderCategories(await api.articleCategories(genealogyId));
}
if (articleId && query('[data-article-detail-page], [data-article-edit-page]')) {
if (query('[data-article-detail-page]')) {
renderArticleDetail(await api.articleDetail(genealogyId, articleId));
} else {
fillArticleForm(await api.articleDetail(genealogyId, articleId));
}
}
} catch (error) {
showMessage(error.message || '谱文数据加载失败');
}
}
async function submitArticle(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var articleId = getCurrentArticleId();
var body = buildArticleBody(getFormValues(form));
if (!api || !genealogyId) {
showMessage('请从具体家谱进入谱文编辑');
return;
}
if (!body.articleTitle || !body.articleContent) {
showMessage('请填写谱文标题和正文');
return;
}
try {
if (articleId) {
await api.updateArticle(genealogyId, articleId, body);
} else {
await api.createArticle(genealogyId, body);
}
showMessage('谱文已保存');
root.location.href = 'profile-article.html?genealogyId=' + encodeURIComponent(genealogyId);
} catch (error) {
showMessage(error.message || '保存谱文失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('submit', function (event) {
var form = event.target.closest('[data-article-form]');
if (!form) return;
event.preventDefault();
submitArticle(form);
});
}
function init() {
if (!documentRef) return;
bindActions();
loadArticles();
}
return {
normalizeList: normalizeList,
buildArticleBody: buildArticleBody,
buildArticleRow: buildArticleRow,
formatStatus: formatStatus,
init: init
};
});
+342
View File
@@ -0,0 +1,342 @@
(function (root, factory) {
// 认证页脚本同时给浏览器页面和 Node 测试使用。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.AuthPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.AuthPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function hashPassword(value) {
// 接口文档要求密码传 32 位 MD5;没有加载 md5.js 时保留原值,便于测试定位。
if (root.hex_md5) return root.hex_md5(value);
if (root.md5) return root.md5(value);
return value;
}
function buildLoginBody(values, hashFn) {
// 这里只组装页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。
var hash = hashFn || hashPassword;
var body = {
phone: values.phone,
password: hash(values.password)
};
if (values.validToken) body.validToken = values.validToken;
return body;
}
function buildSmsLoginBody(values) {
// 短信登录接口只需要页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。
var body = {
phone: values.phone,
smsCode: values.smsCode
};
if (values.validToken) body.validToken = values.validToken;
return body;
}
function buildRegisterBody(values, hashFn) {
// 注册验证码当前对应 validToken,可选字段只在有值时提交。
var hash = hashFn || hashPassword;
var body = {
phone: values.phone,
password: hash(values.password),
nickName: values.nickName || values.phone
};
if (values.validToken) body.validToken = values.validToken;
return body;
}
function buildPasswordResetBody(values, hashFn) {
// 找回密码接口使用 newPassword 字段,页面确认密码只在提交前校验,不传给后端。
var hash = hashFn || hashPassword;
return {
phone: values.phone,
smsCode: values.smsCode,
newPassword: hash(values.newPassword),
validToken: values.validToken || ''
};
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function getCaptchaScene(formType) {
// PC/H5 认证场景来自后台验证码配置,客户端 Key 为 web_pc。
var map = {
login: 'WEB_H5_LOGIN',
register: 'WEB_H5_REGISTER',
'password-reset': 'WEB_H5_FORGOT_PASSWORD'
};
return map[formType] || '';
}
function getFormCaptchaScene(form, formType) {
// 页面可通过 data-captcha-scene 显式声明后台配置的验证码场景。
var sceneCode = form && form.getAttribute && form.getAttribute('data-captcha-scene');
return sceneCode || getCaptchaScene(formType);
}
async function ensureCaptcha(form, sceneCode, subject) {
// 未配置 PC/H5 验证场景时不调用验证中心,也不伪造 validToken。
if (!sceneCode) return true;
// 验证适配层不存在时不阻塞测试环境;真实页面会加载 captcha-pages.js。
if (!root.CaptchaPages || !root.CaptchaPages.ensureToken) return true;
return Boolean(await root.CaptchaPages.ensureToken(form, {
sceneCode: sceneCode,
subject: subject
}));
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function readForm(form) {
// 页面通过 name 属性和接口字段对齐,避免按 placeholder 文案取值。
var values = {};
queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) {
values[field.name] = String(field.value || '').trim();
});
return values;
}
function setBusy(button, busy) {
// 加载状态只切 class 和 aria,不在 JS 中写样式。
if (!button) return;
button.classList.toggle('is-loading', busy);
if ('disabled' in button) button.disabled = busy;
button.setAttribute('aria-busy', busy ? 'true' : 'false');
}
function showMessage(message) {
if (root.MessageUtil && root.MessageUtil.show) {
root.MessageUtil.show(message);
return;
}
if (root.console && root.console.warn) root.console.warn(message);
}
function requireValue(value, message) {
if (value) return true;
showMessage(message);
return false;
}
function bindSubmit(selector, handler) {
// 所有认证表单统一拦截 submit,避免链接跳转绕过接口请求。
var form = query(selector);
if (!form) return;
form.addEventListener('submit', function (event) {
event.preventDefault();
handler(form, query('[type="submit"]', form));
});
}
function setLoginMode(activeMode) {
// 登录方式切换只更新状态,具体样式由 login.css 控制。
queryAll('[data-login-mode-tab]').forEach(function (button) {
var isActive = button.getAttribute('data-login-mode-tab') === activeMode;
button.classList.toggle('is-active', isActive);
button.setAttribute('aria-selected', isActive ? 'true' : 'false');
});
queryAll('[data-login-mode-panel]').forEach(function (panel) {
var isActive = panel.getAttribute('data-login-mode-panel') === activeMode;
panel.hidden = !isActive;
});
}
async function submitLogin(form, button) {
// 登录成功后跳转地址由 HTML 的 data-success-url 控制,页面可单独调整。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.password, '请填写密码')) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'login'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.login(buildLoginBody(values));
root.location.href = form.getAttribute('data-success-url') || 'profile.html';
} catch (error) {
showMessage(error.message || '登录失败');
} finally {
setBusy(button, false);
}
}
async function submitSmsLogin(form, button) {
// PC 短信登录与密码登录共用 WEB_H5_LOGIN 场景,短信码由同页按钮发送。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.smsCode, '请填写短信验证码')) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'login'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.loginBySms(buildSmsLoginBody(values));
root.location.href = form.getAttribute('data-success-url') || 'profile.html';
} catch (error) {
showMessage(error.message || '短信登录失败');
} finally {
setBusy(button, false);
}
}
async function submitRegister(form, button) {
// 注册成功后默认进入创建家谱流程。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.password, '请填写密码')) return;
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'register'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.register(buildRegisterBody(values));
root.location.href = form.getAttribute('data-success-url') || 'create-genealogy.html';
} catch (error) {
showMessage(error.message || '注册失败');
} finally {
setBusy(button, false);
}
}
async function sendCode(button) {
// 当前只接找回密码验证码;后续注册短信可复用 data-scene-code 扩展。
var api = getApi();
var form = button.closest('form');
var values = readForm(form);
var sceneCode = button.getAttribute('data-scene-code') || getFormCaptchaScene(form, 'password-reset');
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!await ensureCaptcha(form, sceneCode, values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.sendSmsCode({
sceneCode: sceneCode,
phone: values.phone,
validToken: values.validToken || ''
});
showMessage('验证码已发送');
} catch (error) {
showMessage(error.message || '验证码发送失败');
} finally {
setBusy(button, false);
}
}
async function submitPasswordReset(form, button) {
// 前端先校验两次密码一致,再按接口字段提交重置请求。
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!requireValue(values.phone, '请填写手机号')) return;
if (!requireValue(values.smsCode, '请填写验证码')) return;
if (!requireValue(values.newPassword, '请填写新密码')) return;
if (values.newPassword !== values.confirmPassword) {
showMessage('两次输入的新密码不一致');
return;
}
if (!await ensureCaptcha(form, getFormCaptchaScene(form, 'password-reset'), values.phone)) return;
values = readForm(form);
setBusy(button, true);
try {
await api.resetPassword(buildPasswordResetBody(values));
showMessage('密码已重设,请重新登录');
root.location.href = form.getAttribute('data-success-url') || 'login.html';
} catch (error) {
showMessage(error.message || '重设密码失败');
} finally {
setBusy(button, false);
}
}
function bindCodeButtons() {
queryAll('[data-api-send-code]').forEach(function (button) {
button.addEventListener('click', function (event) {
event.preventDefault();
sendCode(button);
});
});
}
function bindLoginModeTabs() {
queryAll('[data-login-mode-tab]').forEach(function (button) {
button.addEventListener('click', function () {
setLoginMode(button.getAttribute('data-login-mode-tab'));
});
});
}
function init() {
// 每个页面只会命中自己的 data-auth-form,不需要拆多个入口文件。
if (!documentRef) return;
bindSubmit('[data-auth-form="login"]', submitLogin);
bindSubmit('[data-auth-form="sms-login"]', submitSmsLogin);
bindSubmit('[data-auth-form="register"]', submitRegister);
bindSubmit('[data-auth-form="password-reset"]', submitPasswordReset);
bindLoginModeTabs();
bindCodeButtons();
}
return {
buildLoginBody: buildLoginBody,
buildSmsLoginBody: buildSmsLoginBody,
buildRegisterBody: buildRegisterBody,
buildPasswordResetBody: buildPasswordResetBody,
getCaptchaScene: getCaptchaScene,
getFormCaptchaScene: getFormCaptchaScene,
init: init
};
});
+261
View File
@@ -0,0 +1,261 @@
(function (root, factory) {
// 验证码适配层同时支持浏览器运行和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.CaptchaPages = factory(root);
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var DEFAULT_SCENE_CODE = 'WEB_H5_LOGIN';
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || root.document).querySelector(selector);
}
function showMessage(message) {
if (root.MessageUtil && root.MessageUtil.show) {
root.MessageUtil.show(message);
return;
}
if (root.console && root.console.warn) root.console.warn(message);
}
function buildChallengeBody(options, api) {
// 请求体字段严格对应 PC YAML 的 VerificationChallengeBody。
var settings = options || {};
return {
tenantId: settings.tenantId || api.tenantId,
clientId: settings.clientId || api.clientId,
sceneCode: settings.sceneCode || DEFAULT_SCENE_CODE,
subject: settings.subject || ''
};
}
function normalizeChallengeData(data) {
// TAC 需要 data.type、data.id 和图片字段;接口文档返回 captchaType、challengeId、payload。
var source = data || {};
var payload = source.payload || {};
return Object.assign({}, payload, {
type: source.captchaType || payload.type || 'SLIDER',
id: payload.id || source.challengeId,
challengeId: source.challengeId,
providerCode: source.providerCode || 'tianai',
captchaType: source.captchaType || payload.type || 'SLIDER'
});
}
function adaptChallengeResponse(response) {
var payload = response || {};
if (Number(payload.code) !== 200 || !payload.data) return payload;
return Object.assign({}, payload, {
data: normalizeChallengeData(payload.data)
});
}
function buildVerifyBody(requestData, context, api) {
// requestData 是 TAC 拖动完成后的轨迹数据;context 保存本次挑战的接口字段。
var settings = context || {};
var source = requestData || {};
return {
tenantId: settings.tenantId || api.tenantId,
clientId: settings.clientId || api.clientId,
sceneCode: settings.sceneCode || DEFAULT_SCENE_CODE,
subject: settings.subject || '',
challengeId: settings.challengeId || source.challengeId || source.id,
providerCode: settings.providerCode || 'tianai',
captchaType: settings.captchaType || 'SLIDER',
payload: {
id: source.id,
data: source.data || {}
}
};
}
function normalizeVerifyResponse(response) {
// TAC 只看 code 是否为 200;接口通过 passed 表示业务验证结果。
var payload = response || {};
if (Number(payload.code) === 200 && payload.data && payload.data.passed === false) {
return Object.assign({}, payload, {
code: 4001,
msg: payload.data.message || payload.msg || '验证失败'
});
}
return payload;
}
function extractValidToken(response) {
var payload = response || {};
var data = payload.data || {};
if (Number(payload.code) !== 200) return '';
if (data.passed === false) return '';
return data.validToken || payload.validToken || '';
}
function getTokenField(form) {
return query('input[name="validToken"]', form);
}
function getCaptchaBox(form) {
// 优先使用页面级弹窗挂载点,避免 TAC 验证码挤占表单布局。
return query('.auth-captcha-modal[data-captcha-box]')
|| query('[data-captcha-box]', form)
|| query('[data-captcha-box]');
}
function writeValidToken(form, token) {
var field = getTokenField(form);
if (field) field.value = token || '';
}
function shouldRequireCaptcha(result) {
// /captcha/require 返回 required=false 时允许直接继续业务请求。
if (!result) return true;
return result.required !== false;
}
function createTac(form, options, api, resolve, reject) {
var settings = options || {};
var box = getCaptchaBox(form);
var context = buildChallengeBody(settings, api);
var lastChallenge = {};
var captcha;
var config;
if (!root.TAC || !root.CaptchaConfig) {
reject(new Error('滑动验证组件未加载'));
return null;
}
if (!box) {
reject(new Error('缺少滑动验证容器'));
return null;
}
config = new root.CaptchaConfig({
bindEl: box,
requestCaptchaDataUrl: api.buildApiUrl('/captcha/challenge'),
validCaptchaUrl: api.buildApiUrl('/captcha/verify'),
requestHeaders: {
clientid: api.clientId
},
validSuccess: function (response, captchaControl, captchaInstance) {
var token = extractValidToken(response);
if (!token) {
reject(new Error('滑动验证未返回有效票据'));
return;
}
writeValidToken(form, token);
if (captchaInstance && captchaInstance.destroyWindow) captchaInstance.destroyWindow();
resolve(token);
},
validFail: function (response, captchaControl, captchaInstance) {
if (captchaInstance && captchaInstance.reloadCaptcha) captchaInstance.reloadCaptcha();
showMessage((response && (response.msg || response.message)) || '滑动验证失败,请重试');
},
btnCloseFun: function (event, captchaInstance) {
if (captchaInstance) captchaInstance.destroyWindow();
reject(new Error('已取消滑动验证'));
}
});
config.addRequestChain({
preRequest: function (type, request) {
if (type === 'requestCaptchaData') {
request.data = buildChallengeBody(settings, api);
}
if (type === 'validCaptcha') {
request.data = buildVerifyBody(request.data, Object.assign({}, context, lastChallenge), api);
}
return true;
},
postRequest: function (type, request, response) {
if (type === 'requestCaptchaData') {
var adapted = adaptChallengeResponse(response);
lastChallenge = {
challengeId: adapted.data && adapted.data.challengeId,
providerCode: adapted.data && adapted.data.providerCode,
captchaType: adapted.data && adapted.data.captchaType
};
Object.keys(response || {}).forEach(function (key) {
delete response[key];
});
Object.assign(response, adapted);
}
if (type === 'validCaptcha') {
Object.assign(response, normalizeVerifyResponse(response));
}
return true;
}
});
captcha = new root.TAC(config, {
bgUrl: 'public/tac/images/dun.jpeg',
logoUrl: null
});
captcha.init();
return captcha;
}
async function ensureToken(form, options) {
// 认证页提交前调用:已有 validToken 直接通过,否则按验证中心策略弹出 TAC。
var api = getApi();
var field = getTokenField(form);
var settings = options || {};
var requirement;
if (!api) return '';
if (field && field.value) return field.value;
if (!settings.sceneCode) return '';
try {
requirement = await api.captchaRequirement(buildChallengeBody(settings, api));
if (!shouldRequireCaptcha(requirement)) return 'CAPTCHA_NOT_REQUIRED';
} catch (error) {
// 查询策略失败时仍尝试直接拉起验证,避免认证接口缺少 validToken。
requirement = null;
}
return new Promise(function (resolve, reject) {
createTac(form, settings, api, resolve, reject);
}).catch(function (error) {
showMessage(error.message || '请先完成滑动验证');
return '';
});
}
return {
buildChallengeBody: buildChallengeBody,
adaptChallengeResponse: adaptChallengeResponse,
buildVerifyBody: buildVerifyBody,
getCaptchaBox: getCaptchaBox,
extractValidToken: extractValidToken,
normalizeVerifyResponse: normalizeVerifyResponse,
shouldRequireCaptcha: shouldRequireCaptcha,
ensureToken: ensureToken
};
});
+384
View File
@@ -0,0 +1,384 @@
(function (root, factory) {
// 祭祀/贺礼/功德模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.CeremonyPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.CeremonyPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
var selectedCeremonyId = '';
function normalizeList(data) {
// 通用列表响应兼容数组、rows、records、list、data。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 标题、姓名和说明进入 innerHTML 前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
var page = query('[data-ceremony-page], [data-ceremony-edit-page], [data-merit-page], [data-merit-edit-page]');
return getQueryParam(search, 'genealogyId') ||
(page && page.getAttribute('data-genealogy-id')) ||
getQueryParam(search, 'id') ||
'';
}
function getCurrentCeremonyId() {
var search = root.location && root.location.search;
return getQueryParam(search, 'ceremonyId') || '';
}
function trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function toNumber(value, fallback) {
var text = String(value === undefined || value === null ? '' : value).trim();
var number = Number(text);
return text && Number.isFinite(number) ? number : fallback;
}
function buildCeremonyBody(values) {
// CeremonyBody 来自 APP.openapi.jsonceremonyType 和 ceremonyTitle 必填。
var source = values || {};
return {
ceremonyType: String(source.ceremonyType || '').trim(),
ceremonyTitle: String(source.ceremonyTitle || '').trim(),
ceremonyDesc: trimOrUndefined(source.ceremonyDesc),
ceremonyTime: trimOrUndefined(source.ceremonyTime),
location: trimOrUndefined(source.location),
coverOssId: toNumber(source.coverOssId),
sortOrder: toNumber(source.sortOrder, 1),
status: String(source.status || '').trim() || '0'
};
}
function buildGiftBody(values) {
var source = values || {};
return {
giverName: trimOrUndefined(source.giverName),
giftAmount: toNumber(source.giftAmount, 0),
giftMessage: trimOrUndefined(source.giftMessage)
};
}
function buildMeritBody(values) {
// MeritRecordBody 来自 APP.openapi.jsondonorName 和 meritTitle 必填。
var source = values || {};
return {
donorName: String(source.donorName || '').trim(),
meritType: String(source.meritType || '').trim() || 'donation',
meritTitle: String(source.meritTitle || '').trim(),
meritContent: trimOrUndefined(source.meritContent),
amount: toNumber(source.amount, 0),
meritTime: trimOrUndefined(source.meritTime),
sortOrder: toNumber(source.sortOrder, 1),
status: String(source.status || '').trim() || '0'
};
}
function getCeremonyId(item) {
return pick(item, ['ceremonyId', 'id'], '');
}
function buildCeremonyRow(item) {
var ceremonyId = getCeremonyId(item);
var title = pick(item, ['ceremonyTitle', 'title'], '未命名活动');
var type = pick(item, ['ceremonyType', 'type'], 'ceremony');
var time = pick(item, ['ceremonyTime', 'time', 'createTime'], '未设置时间');
var giftCount = pick(item, ['giftCount', 'gifts'], '0');
return '<button class="module-row ceremony-row" type="button" data-ceremony-id="' + escapeHtml(ceremonyId) + '">' +
'<div><h3>' + escapeHtml(title) + '</h3><p>' + escapeHtml(type) + ' · ' + escapeHtml(time) + ' · ' + escapeHtml(giftCount) + ' 条献礼</p></div>' +
'<span class="pill">管理</span></button>';
}
function buildGiftRow(item) {
var name = pick(item, ['giverName', 'memberName', 'name'], '匿名');
var amount = pick(item, ['giftAmount', 'amount'], '0');
var message = pick(item, ['giftMessage', 'message'], '暂无留言');
return '<div class="module-row ceremony-gift-row"><div><h3>' + escapeHtml(name) + '</h3><p>' + escapeHtml(amount) + ' 元 · ' + escapeHtml(message) + '</p></div><span class="pill">献礼</span></div>';
}
function buildMeritRow(item) {
var name = pick(item, ['donorName', 'name'], '未命名功德人');
var title = pick(item, ['meritTitle', 'title'], '功德记录');
var time = pick(item, ['meritTime', 'createTime'], '未记录时间');
return '<div class="module-row merit-row"><div><h3>' + escapeHtml(name) + '</h3><p>' + escapeHtml(title) + ' · ' + escapeHtml(time) + '</p></div><span class="pill">查看</span></div>';
}
function renderCeremonies(items) {
var container = query('[data-ceremony-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无贺礼邀请</div>';
return;
}
container.innerHTML = list.map(buildCeremonyRow).join('');
}
function renderGifts(items) {
var container = query('[data-ceremony-gift-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无献礼记录</div>';
return;
}
container.innerHTML = list.map(buildGiftRow).join('');
}
function renderMerits(items) {
var container = query('[data-merit-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无功德记录</div>';
return;
}
container.innerHTML = list.map(buildMeritRow).join('');
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
values[field.name] = field.value;
});
return values;
}
async function loadCeremonies() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var ceremonyId = getCurrentCeremonyId();
if (!api || !genealogyId) return;
try {
if (query('[data-ceremony-list]')) {
renderCeremonies(await api.ceremonies(genealogyId));
}
if (ceremonyId && query('[data-ceremony-gift-list]')) {
selectedCeremonyId = ceremonyId;
renderGifts(await api.ceremonyGifts(genealogyId, ceremonyId));
}
} catch (error) {
showMessage(error.message || '贺礼数据加载失败');
}
}
async function loadMerits() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
if (!api || !genealogyId || !query('[data-merit-list]')) return;
try {
renderMerits(await api.meritRecords(genealogyId));
} catch (error) {
showMessage(error.message || '功德记录加载失败');
}
}
async function submitCeremony(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var ceremonyId = getCurrentCeremonyId();
var body = buildCeremonyBody(getFormValues(form));
if (!api || !genealogyId) return;
if (!body.ceremonyType || !body.ceremonyTitle) {
showMessage('请填写贺礼类型和标题');
return;
}
try {
if (ceremonyId) {
await api.updateCeremony(genealogyId, ceremonyId, body);
} else {
await api.createCeremony(genealogyId, body);
}
showMessage('贺礼已保存');
root.location.href = 'profile-gift.html?genealogyId=' + encodeURIComponent(genealogyId);
} catch (error) {
showMessage(error.message || '保存贺礼失败');
}
}
async function submitGift(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var ceremonyId = selectedCeremonyId || getCurrentCeremonyId();
var body = buildGiftBody(getFormValues(form));
if (!api || !genealogyId || !ceremonyId) {
showMessage('请先选择贺礼邀请');
return;
}
try {
await api.createCeremonyGift(genealogyId, ceremonyId, body);
form.reset();
showMessage('献礼已保存');
renderGifts(await api.ceremonyGifts(genealogyId, ceremonyId));
} catch (error) {
showMessage(error.message || '保存献礼失败');
}
}
async function submitMerit(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var body = buildMeritBody(getFormValues(form));
if (!api || !genealogyId) return;
if (!body.donorName || !body.meritTitle) {
showMessage('请填写功德人和功德标题');
return;
}
try {
await api.createMeritRecord(genealogyId, body);
showMessage('功德记录已保存');
root.location.href = 'profile-merit.html?genealogyId=' + encodeURIComponent(genealogyId);
} catch (error) {
showMessage(error.message || '保存功德记录失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('click', function (event) {
var ceremonyButton = event.target.closest('[data-ceremony-id]');
if (!ceremonyButton) return;
selectedCeremonyId = ceremonyButton.getAttribute('data-ceremony-id');
if (getApi() && getCurrentGenealogyId()) {
getApi().ceremonyGifts(getCurrentGenealogyId(), selectedCeremonyId).then(renderGifts).catch(function (error) {
showMessage(error.message || '献礼加载失败');
});
}
});
documentRef.addEventListener('submit', function (event) {
var ceremonyForm = event.target.closest('[data-ceremony-form]');
var giftForm = event.target.closest('[data-ceremony-gift-form]');
var meritForm = event.target.closest('[data-merit-form]');
if (ceremonyForm) {
event.preventDefault();
submitCeremony(ceremonyForm);
}
if (giftForm) {
event.preventDefault();
submitGift(giftForm);
}
if (meritForm) {
event.preventDefault();
submitMerit(meritForm);
}
});
}
function init() {
if (!documentRef) return;
bindActions();
loadCeremonies();
loadMerits();
}
return {
normalizeList: normalizeList,
buildCeremonyBody: buildCeremonyBody,
buildMeritBody: buildMeritBody,
buildCeremonyRow: buildCeremonyRow,
init: init
};
});
+321
View File
@@ -0,0 +1,321 @@
(function (root, factory) {
// 家族圈动态模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.FeedPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.FeedPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 动态分页响应兼容 rows、records、list、data。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 动态内容进入 innerHTML 前转义,详情富文本不在这里处理。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
var page = query('[data-feed-page], [data-feed-edit-page]');
return getQueryParam(search, 'genealogyId') ||
(page && page.getAttribute('data-genealogy-id')) ||
getQueryParam(search, 'id') ||
'';
}
function getCurrentFeedId() {
var search = root.location && root.location.search;
return getQueryParam(search, 'feedId') || '';
}
function trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function toNumber(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
var number = Number(text);
return text && Number.isFinite(number) ? number : undefined;
}
function buildFeedBody(values) {
// FamilyFeedBody 来自 APP.openapi.json。
var source = values || {};
return {
content: String(source.content || '').trim(),
mediaOssIds: trimOrUndefined(source.mediaOssIds),
visibility: String(source.visibility || '').trim() || '2',
status: String(source.status || '').trim() || '0'
};
}
function buildCommentBody(values) {
// FamilyFeedCommentBody 要求 content 必填。
var source = values || {};
return {
parentCommentId: toNumber(source.parentCommentId),
replyUserId: toNumber(source.replyUserId),
content: String(source.content || '').trim()
};
}
function getFeedId(item) {
return pick(item, ['feedId', 'id'], '');
}
function buildFeedRow(item) {
var feedId = getFeedId(item);
var author = pick(item, ['authorName', 'nickName', 'memberName'], '家族成员');
var content = pick(item, ['content', 'feedContent'], '暂无内容');
var likeCount = pick(item, ['likeCount', 'likes'], '0');
var commentCount = pick(item, ['commentCount', 'comments'], '0');
var createTime = pick(item, ['createTime', 'publishTime', 'updateTime'], '未记录时间');
return '<div class="module-row feed-row" data-feed-id="' + escapeHtml(feedId) + '">' +
'<div><h3>' + escapeHtml(author) + '</h3><p>' + escapeHtml(content) + '</p><small>' + escapeHtml(createTime) + ' · ' + escapeHtml(likeCount) + ' 赞 · ' + escapeHtml(commentCount) + ' 评论</small></div>' +
'<div class="row-actions"><button class="pill" type="button" data-feed-like="' + escapeHtml(feedId) + '">点赞</button><button class="pill" type="button" data-feed-comment="' + escapeHtml(feedId) + '">评论</button></div>' +
'</div>';
}
function renderFeeds(items) {
var container = query('[data-feed-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无家族动态</div>';
return;
}
container.innerHTML = list.map(buildFeedRow).join('');
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
if (field.type === 'checkbox') {
values[field.name] = field.checked ? '1' : '';
return;
}
values[field.name] = field.value;
});
return values;
}
function fillFeedForm(feed) {
var source = feed || {};
queryAll('[name]').forEach(function (field) {
var value = pick(source, [field.name], '');
if (field.type === 'checkbox') {
field.checked = value === '1' || value === true;
return;
}
if (value !== '') field.value = value;
});
}
async function loadFeeds() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var feedId = getCurrentFeedId();
if (!api || !genealogyId) return;
try {
if (query('[data-feed-list]')) {
renderFeeds(await api.feedsPage(genealogyId, {
pageNum: 1,
pageSize: 10
}));
}
if (feedId && query('[data-feed-edit-page]')) {
fillFeedForm(await api.feedDetail(genealogyId, feedId));
}
} catch (error) {
showMessage(error.message || '家族动态加载失败');
}
}
async function submitFeed(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var feedId = getCurrentFeedId();
var body = buildFeedBody(getFormValues(form));
if (!api || !genealogyId) {
showMessage('请从具体家谱进入动态发布');
return;
}
if (!body.content) {
showMessage('请填写动态内容');
return;
}
try {
if (feedId) {
await api.updateFeed(genealogyId, feedId, body);
} else {
await api.createFeed(genealogyId, body);
}
showMessage('动态已保存');
root.location.href = 'profile-feed.html?genealogyId=' + encodeURIComponent(genealogyId);
} catch (error) {
showMessage(error.message || '保存动态失败');
}
}
async function likeFeed(feedId) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
if (!api || !genealogyId || !feedId) return;
try {
await api.likeFeed(genealogyId, feedId);
showMessage('已点赞');
loadFeeds();
} catch (error) {
showMessage(error.message || '点赞失败');
}
}
async function commentFeed(feedId) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var content;
if (!api || !genealogyId || !feedId) return;
content = root.prompt('请输入评论内容', '');
if (!content) return;
try {
await api.createFeedComment(genealogyId, feedId, buildCommentBody({
content: content
}));
showMessage('评论已发布');
loadFeeds();
} catch (error) {
showMessage(error.message || '评论失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('submit', function (event) {
var form = event.target.closest('[data-feed-form]');
if (!form) return;
event.preventDefault();
submitFeed(form);
});
documentRef.addEventListener('click', function (event) {
var likeButton = event.target.closest('[data-feed-like]');
var commentButton = event.target.closest('[data-feed-comment]');
if (likeButton) {
event.preventDefault();
likeFeed(likeButton.getAttribute('data-feed-like'));
}
if (commentButton) {
event.preventDefault();
commentFeed(commentButton.getAttribute('data-feed-comment'));
}
});
}
function init() {
if (!documentRef) return;
bindActions();
loadFeeds();
}
return {
normalizeList: normalizeList,
buildFeedBody: buildFeedBody,
buildCommentBody: buildCommentBody,
buildFeedRow: buildFeedRow,
init: init
};
});
+177
View File
@@ -0,0 +1,177 @@
(function (root, factory) {
// 反馈模块同时支持浏览器页面和 Node 测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.FeedbackPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.FeedbackPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 兼容分页 rows、records、list 和直接数组。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function buildFeedbackBody(values) {
// 接口只接收类型、内容、联系方式;页面上的标题合并到内容前面。
var title = values.feedbackTitle ? '【' + values.feedbackTitle + '】' : '';
return {
feedbackType: values.feedbackType || 'suggestion',
feedbackContent: title + (values.feedbackContent || ''),
contactInfo: values.contactInfo || ''
};
}
function buildFeedbackRow(item) {
var type = pick(item, ['feedbackType', 'typeName'], '反馈');
var content = pick(item, ['feedbackContent', 'content'], '暂无描述');
var status = pick(item, ['status', 'statusName'], '已提交');
var time = pick(item, ['createTime', 'createDate', 'createdAt'], '时间待确认');
return '<div class="module-row"><div><h3>' + type + '</h3><p>' +
status + ' · ' + time + ' · ' + content +
'</p></div><span class="pill">查看</span></div>';
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function readForm(form) {
// name 属性和反馈接口字段保持一致,减少页面文案变化带来的影响。
var values = {};
queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) {
values[field.name] = String(field.value || '').trim();
});
return values;
}
function setBusy(button, busy) {
// 加载态只切换 class,具体样式在 CSS 中维护。
if (!button) return;
button.classList.toggle('is-loading', busy);
if ('disabled' in button) button.disabled = busy;
button.setAttribute('aria-busy', busy ? 'true' : 'false');
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function renderFeedbackList(items) {
var container = query('[data-feedback-list]');
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">暂无历史反馈</div>';
return;
}
container.innerHTML = items.map(buildFeedbackRow).join('');
}
async function loadFeedbackList() {
var api = getApi();
var container = query('[data-feedback-list]');
if (!api || !container) return;
container.classList.add('is-loading');
try {
renderFeedbackList(normalizeList(await api.feedbackList()));
} catch (error) {
showMessage(error.message || '历史反馈加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function submitFeedback(form, button) {
var api = getApi();
var values = readForm(form);
if (!api) return;
if (!values.feedbackContent) {
showMessage('请填写反馈内容');
return;
}
setBusy(button, true);
try {
await api.submitFeedback(buildFeedbackBody(values));
form.reset();
showMessage('反馈已提交');
loadFeedbackList();
} catch (error) {
showMessage(error.message || '反馈提交失败');
} finally {
setBusy(button, false);
}
}
function bindFeedbackForms() {
queryAll('[data-feedback-form]').forEach(function (form) {
form.addEventListener('submit', function (event) {
event.preventDefault();
submitFeedback(form, query('[type="submit"]', form));
});
});
}
function init() {
if (!documentRef) return;
bindFeedbackForms();
loadFeedbackList();
}
return {
normalizeList: normalizeList,
buildFeedbackBody: buildFeedbackBody,
buildFeedbackRow: buildFeedbackRow,
init: init
};
});
+408
View File
@@ -0,0 +1,408 @@
(function (root, factory) {
// 家谱模块脚本同时支持浏览器运行和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.GenealogyPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.GenealogyPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 列表接口可能返回数组、rows、records 或 list,这里统一成数组供页面渲染。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
// 后端字段名存在少量差异时,按优先级取第一个可用字段。
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function buildGenealogyBody(values) {
// 创建家谱接口必填 genealogyName、surname、regionCode,地区编码由地区选择器回填。
var source = values || {};
return {
genealogyName: source.genealogyName || '',
surname: source.surname || '',
regionCode: source.regionCode || '',
addressDetail: source.addressDetail || '',
intro: source.intro || '',
visibility: source.visibility || '1',
joinMode: source.joinMode || '1'
};
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function readForm(form) {
// 页面表单 name 与接口字段对齐,后续加字段只需要补 HTML。
var values = {};
queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) {
values[field.name] = String(field.value || '').trim();
});
return values;
}
function getQueryParam(search, name) {
// 详情页和加入页用 query 参数传递 genealogyId,测试环境也可直接传 search 字符串。
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
return getQueryParam(search, 'id') || getQueryParam(search, 'genealogyId');
}
function escapeHtml(value) {
// 接口内容进入 innerHTML 前统一转义,避免公开页面渲染异常内容。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function setBusy(button, busy) {
// 加载状态只加 class,样式统一写在 CSS 中。
if (!button) return;
button.classList.toggle('is-loading', busy);
if ('disabled' in button) button.disabled = busy;
button.setAttribute('aria-busy', busy ? 'true' : 'false');
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function requireValue(value, message) {
if (value) return true;
showMessage(message);
return false;
}
function buildFamilyHeroTitle(detail) {
return pick(detail, ['genealogyName', 'name', 'title'], '未命名家谱');
}
function formatJoinMode(value) {
// APP.openapi.json 中 joinMode 为枚举值,页面展示时转成中文。
var map = {
0: '关闭加入',
1: '审核加入',
2: '邀请码加入'
};
return map[value] || value || '多人共修';
}
function buildFamilySummary(detail) {
// 后端详情字段可能来自统计字段或普通展示字段,这里集中做字段映射。
var memberCount = pick(detail, ['memberCount', 'membersCount'], '0');
var generationCount = pick(detail, ['generationCount', 'generationTotal'], '0');
var articleCount = pick(detail, ['articleCount', 'articlesCount'], '0');
var albumCount = pick(detail, ['albumCount', 'albumsCount'], '0');
return [
{ label: '成员', value: memberCount },
{ label: '世代', value: generationCount },
{ label: '谱文', value: articleCount },
{ label: '相册', value: albumCount }
];
}
function buildJoinApplyBody(values) {
// 接口文档要求 applicantName、phone、relationDesc、applyReason,可选 inviterUserId。
var source = values || {};
var inviterUserId = source.inviterUserId ? Number(source.inviterUserId) : undefined;
return {
applicantName: String(source.applicantName || '').trim(),
phone: String(source.phone || '').trim(),
relationDesc: String(source.relationDesc || '').trim(),
applyReason: String(source.applyReason || '').trim() || '申请加入家谱',
inviterUserId: inviterUserId
};
}
function renderPublicGenealogies(items) {
// 广场页复用现有卡片结构,只替换接口返回的数据。
var container = query('[data-genealogy-list="public"]');
var html;
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">暂无公开家谱</div>';
return;
}
html = items.slice(0, 6).map(function (item, index) {
// 第一条沿用推荐卡片样式,其余走普通家谱卡片样式。
var id = pick(item, ['genealogyId', 'id'], '');
var name = pick(item, ['genealogyName', 'name', 'title'], '未命名家谱');
var surname = pick(item, ['surname'], '家谱');
var region = pick(item, ['addressDetail', 'regionName', 'originPlace'], '地区待完善');
var intro = pick(item, ['intro', 'description', 'summary'], '家谱资料正在完善中。');
var className = index === 0 ? 'family-card featured tilt-card' : 'card family-lite tilt-card';
return '<a class="' + className + '" href="family-detail.html?id=' + encodeURIComponent(id) + '">' +
'<h3>' + name + '</h3>' +
'<p>' + intro + '</p>' +
'<div class="tag-row"><span class="tag">' + region + '</span><span class="tag">' + surname + '</span></div>' +
'</a>';
}).join('');
container.innerHTML = html;
}
function buildMyGenealogyRow(item) {
// 我的家谱列表保持 profile-module.css 的 module-row 结构。
var id = pick(item, ['genealogyId', 'id'], '');
var name = pick(item, ['genealogyName', 'name'], '未命名家谱');
var role = pick(item, ['roleName', 'memberRole'], '成员');
var count = pick(item, ['memberCount', 'membersCount'], '0');
return '<a class="module-row" href="profile-family-home.html?id=' + encodeURIComponent(id) + '">' +
'<div><h3>' + name + '</h3><p>' + role + ' · 共修入 ' + count + ' 人</p></div>' +
'<span class="pill">进入主页</span></a>';
}
function renderMyGenealogies(items) {
// 没有数据时显示公共空状态,避免保留旧静态示例误导用户。
var container = query('[data-genealogy-list="mine"]');
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">你还没有创建或加入家谱</div>';
return;
}
container.innerHTML = items.map(buildMyGenealogyRow).join('');
}
function renderFamilyDetail(detail) {
var page = query('[data-family-detail]');
var summaryGrid = query('[data-family-summary]');
var joinLink = query('[data-family-join-link]');
var id;
if (!page) return;
id = pick(detail, ['genealogyId', 'id'], getCurrentGenealogyId());
queryAll('[data-family-text]').forEach(function (node) {
var field = node.getAttribute('data-family-text');
var fallback = node.getAttribute('data-fallback') || '';
var value = field === 'title'
? buildFamilyHeroTitle(detail)
: pick(detail, field.split('|'), fallback);
node.textContent = field.indexOf('joinMode') !== -1 ? formatJoinMode(value) : value;
});
if (summaryGrid) {
summaryGrid.innerHTML = buildFamilySummary(detail).map(function (item) {
return '<span><b>' + escapeHtml(item.value) + '</b>' + escapeHtml(item.label) + '</span>';
}).join('');
}
if (joinLink && id) {
joinLink.href = 'join-genealogy.html?id=' + encodeURIComponent(id);
}
}
async function loadFamilyDetail() {
// 只有详情页存在 data-family-detail,且 URL 带 id/genealogyId 时才请求接口。
var api = getApi();
var page = query('[data-family-detail]');
var genealogyId = getCurrentGenealogyId();
if (!api || !page) return;
if (!genealogyId) {
showMessage('缺少家谱ID');
return;
}
page.classList.add('is-loading');
try {
renderFamilyDetail(await api.genealogyDetail(genealogyId));
} catch (error) {
showMessage(error.message || '家谱详情加载失败');
} finally {
page.classList.remove('is-loading');
}
}
async function submitJoinApply(form, button) {
var api = getApi();
var values = readForm(form);
var genealogyId = values.genealogyId || getCurrentGenealogyId();
var body = buildJoinApplyBody(values);
if (!api) return;
if (!requireValue(genealogyId, '请填写家谱ID或邀请码')) return;
if (!requireValue(body.applicantName, '请填写你的姓名')) return;
if (!requireValue(body.phone, '请填写手机号')) return;
if (!requireValue(body.relationDesc, '请填写关系说明')) return;
setBusy(button, true);
try {
await api.applyJoinGenealogy(genealogyId, body);
showMessage('申请已提交,请等待管理员审核');
root.location.href = form.getAttribute('data-success-url') || 'profile-join-family.html';
} catch (error) {
showMessage(error.message || '申请提交失败');
} finally {
setBusy(button, false);
}
}
async function loadPublicGenealogies() {
// 只有带 data-genealogy-list="public" 的页面才会真正发起请求。
var api = getApi();
var container = query('[data-genealogy-list="public"]');
if (!api || !container) return;
container.classList.add('is-loading');
try {
renderPublicGenealogies(normalizeList(await api.publicGenealogies()));
} catch (error) {
showMessage(error.message || '公开家谱加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function loadMyGenealogies() {
// 只有个人中心“我的家谱”页面会命中这个容器。
var api = getApi();
var container = query('[data-genealogy-list="mine"]');
if (!api || !container) return;
container.classList.add('is-loading');
try {
renderMyGenealogies(normalizeList(await api.myGenealogies()));
} catch (error) {
showMessage(error.message || '我的家谱加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function submitGenealogy(form, button) {
// 创建成功后跳到我的家谱列表,后续继续维护成员和内容。
var api = getApi();
var body = buildGenealogyBody(readForm(form));
if (!api) return;
if (!requireValue(body.genealogyName, '请填写家谱名称')) return;
if (!requireValue(body.surname, '请填写主要姓氏')) return;
if (!requireValue(body.regionCode, '请选择家族所在地')) return;
setBusy(button, true);
try {
await api.createGenealogy(body);
showMessage('家谱创建成功');
root.location.href = form.getAttribute('data-success-url') || 'profile-families.html';
} catch (error) {
showMessage(error.message || '家谱创建失败');
} finally {
setBusy(button, false);
}
}
function bindCreateForm() {
// 创建表单用 submit 事件,兼容按钮点击和键盘回车提交。
var form = query('[data-genealogy-form="create"]');
if (!form) return;
form.addEventListener('submit', function (event) {
event.preventDefault();
submitGenealogy(form, query('[type="submit"]', form));
});
}
function bindJoinForm() {
// 加入页用同一个 submit 入口,兼容详情页带 id 跳转和用户手动输入家谱ID。
var form = query('[data-genealogy-form="join"]');
var idField;
var currentId;
if (!form) return;
idField = query('[name="genealogyId"]', form);
currentId = getCurrentGenealogyId();
if (idField && currentId && !idField.value) idField.value = currentId;
form.addEventListener('submit', function (event) {
event.preventDefault();
submitJoinApply(form, query('[type="submit"]', form));
});
}
function init() {
// 一个模块脚本服务多个家谱页面,通过 data 属性决定当前页面要做什么。
if (!documentRef) return;
bindCreateForm();
bindJoinForm();
loadPublicGenealogies();
loadMyGenealogies();
loadFamilyDetail();
}
return {
normalizeList: normalizeList,
pick: pick,
buildGenealogyBody: buildGenealogyBody,
getQueryParam: getQueryParam,
buildFamilyHeroTitle: buildFamilyHeroTitle,
buildFamilySummary: buildFamilySummary,
formatJoinMode: formatJoinMode,
buildJoinApplyBody: buildJoinApplyBody,
buildMyGenealogyRow: buildMyGenealogyRow,
init: init
};
});
+349
View File
@@ -0,0 +1,349 @@
(function (root, factory) {
// 字辈谱模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.GenerationPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.GenerationPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 通用列表响应可能是数组,也可能包在 rows、records、list 或 data 中。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 字辈文本进入 innerHTML 前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
if (!documentRef) return null;
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
if (!documentRef) return [];
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
if (root.alert) {
root.alert(message);
}
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
var page = query('[data-generation-page]');
return getQueryParam(search, 'id') ||
getQueryParam(search, 'genealogyId') ||
(page && page.getAttribute('data-genealogy-id')) ||
'';
}
function toNumber(value, fallback) {
var number = Number(String(value || '').trim());
return Number.isFinite(number) && number > 0 ? number : fallback;
}
function toBoolean(value) {
return value === true || value === 'true' || value === '1' || value === 'on';
}
function buildGenerationBody(values) {
// GenerationPoemBody 必填 generationNo、generationText,其余字段给接口默认值。
var source = values || {};
var generationNo = toNumber(source.generationNo, 1);
return {
generationNo: generationNo,
generationText: String(source.generationText || '').trim(),
description: String(source.description || '').trim(),
sortOrder: toNumber(source.sortOrder, generationNo),
status: String(source.status || '').trim() || '0'
};
}
function buildGenerationBatchBody(values) {
// GenerationPoemBatchBody 只包含批量文本和是否遇到断代即停止。
var source = values || {};
return {
content: String(source.content || '').trim(),
stopMissingOldGeneration: toBoolean(source.stopMissingOldGeneration)
};
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
if (field.type === 'checkbox') {
values[field.name] = field.checked ? 'on' : '';
return;
}
values[field.name] = field.value;
});
return values;
}
function setBatchStatus(message) {
var statusNode = query('[data-generation-batch-status]');
if (statusNode) {
statusNode.textContent = message || '';
}
}
function buildGenerationRow(item, genealogyId) {
var poemId = pick(item, ['poemId', 'id'], '');
var generationNo = pick(item, ['generationNo', 'generation', 'sortOrder'], '1');
var text = pick(item, ['generationText', 'content', 'text'], '未设置');
var memberCount = pick(item, ['memberCount', 'personCount', 'count'], '0');
var description = pick(item, ['description', 'remark'], '暂无备注');
return '<div class="module-row generation-row" data-generation-id="' + escapeHtml(poemId) + '">' +
'<div><h3>第 ' + escapeHtml(generationNo) + ' 代:' + escapeHtml(text) + '</h3><p>' + escapeHtml(memberCount) + ' 人 · ' + escapeHtml(description) + '</p></div>' +
'<button class="pill" type="button" data-generation-edit="' + escapeHtml(poemId) + '" data-generation-no="' + escapeHtml(generationNo) + '" data-generation-text="' + escapeHtml(text) + '" data-genealogy-id="' + escapeHtml(genealogyId) + '">编辑</button>' +
'</div>';
}
function renderGenerations(items, genealogyId) {
var container = query('[data-generation-list]');
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">暂无字辈记录</div>';
return;
}
container.innerHTML = items.map(function (item) {
return buildGenerationRow(item, genealogyId);
}).join('');
}
function renderBatchPreview(data) {
var container = query('[data-generation-batch-preview]');
var items = normalizeList(data);
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">预览完成,接口未返回明细</div>';
return;
}
container.innerHTML = items.map(function (item) {
return buildGenerationRow(item, getCurrentGenealogyId());
}).join('');
}
async function loadGenerations() {
var api = getApi();
var container = query('[data-generation-list]');
var genealogyId = getCurrentGenealogyId();
if (!api || !container) return;
if (!genealogyId) {
container.innerHTML = '<div class="api-empty">请从具体家谱进入字辈谱</div>';
return;
}
container.classList.add('is-loading');
try {
renderGenerations(normalizeList(await api.generationPoems(genealogyId)), genealogyId);
} catch (error) {
showMessage(error.message || '字辈谱加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function createGeneration() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var generationNo;
var generationText;
var body;
if (!api || !genealogyId) {
showMessage('请从具体家谱进入字辈谱');
return;
}
generationNo = root.prompt('请输入世代序号', '');
if (!generationNo) return;
generationText = root.prompt('请输入字辈内容', '');
if (!generationText) return;
body = buildGenerationBody({
generationNo: generationNo,
generationText: generationText
});
try {
await api.createGenerationPoem(genealogyId, body);
showMessage('字辈已新增');
loadGenerations();
} catch (error) {
showMessage(error.message || '新增字辈失败');
}
}
async function editGeneration(button) {
var api = getApi();
var genealogyId = button.getAttribute('data-genealogy-id') || getCurrentGenealogyId();
var poemId = button.getAttribute('data-generation-edit');
var currentText = button.getAttribute('data-generation-text') || '';
var generationNo = button.getAttribute('data-generation-no') || '1';
var nextText;
if (!api || !genealogyId || !poemId) return;
nextText = root.prompt('请输入新的字辈', currentText);
if (!nextText) return;
button.classList.add('is-loading');
try {
await api.updateGenerationPoem(genealogyId, poemId, buildGenerationBody({
generationNo: generationNo,
generationText: nextText
}));
showMessage('字辈已更新');
loadGenerations();
} catch (error) {
showMessage(error.message || '更新字辈失败');
} finally {
button.classList.remove('is-loading');
}
}
async function submitGenerationBatch(action) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var form = query('[data-generation-batch-form]');
var body;
var result;
if (!api || !genealogyId || !form) {
showMessage('请从具体家谱进入字辈谱');
return;
}
body = buildGenerationBatchBody(getFormValues(form));
if (!body.content) {
showMessage('请填写批量字辈内容');
return;
}
try {
setBatchStatus(action === 'save' ? '批量保存中...' : '批量预览中...');
if (action === 'save') {
await api.saveGenerationPoemsBatch(genealogyId, body);
setBatchStatus('批量保存完成');
showMessage('批量字辈已保存');
loadGenerations();
return;
}
result = await api.previewGenerationPoems(genealogyId, body);
renderBatchPreview(result);
setBatchStatus('批量预览完成');
} catch (error) {
setBatchStatus('批量处理失败');
showMessage(error.message || '批量字辈处理失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('click', function (event) {
var addButton = event.target.closest('[data-generation-add]');
var editButton = event.target.closest('[data-generation-edit]');
var batchButton = event.target.closest('[data-generation-batch-action]');
if (addButton) {
event.preventDefault();
createGeneration();
}
if (editButton) {
event.preventDefault();
editGeneration(editButton);
}
if (batchButton) {
event.preventDefault();
submitGenerationBatch(batchButton.getAttribute('data-generation-batch-action'));
}
});
}
function init() {
if (!documentRef) return;
bindActions();
loadGenerations();
}
return {
normalizeList: normalizeList,
buildGenerationBody: buildGenerationBody,
buildGenerationBatchBody: buildGenerationBatchBody,
buildGenerationRow: buildGenerationRow,
init: init
};
});
+245
View File
@@ -0,0 +1,245 @@
(function (root, factory) {
// 成长记录模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.GrowthPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.GrowthPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 成长记录列表兼容数组、rows、records、list、data。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 成长记录标题和内容进入 innerHTML 前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
var page = query('[data-growth-page], [data-growth-edit-page]');
return getQueryParam(search, 'genealogyId') ||
(page && page.getAttribute('data-genealogy-id')) ||
getQueryParam(search, 'id') ||
'';
}
function getCurrentRecordId() {
var search = root.location && root.location.search;
return getQueryParam(search, 'recordId') || '';
}
function trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function toNumber(value, fallback) {
var text = String(value === undefined || value === null ? '' : value).trim();
var number = Number(text);
return text && Number.isFinite(number) ? number : fallback;
}
function buildGrowthBody(values) {
// GrowthRecordBody 来自 APP.openapi.jsonrecordTitle 必填。
var source = values || {};
return {
lineagePersonId: toNumber(source.lineagePersonId),
recordType: String(source.recordType || '').trim() || 'growth',
recordTitle: String(source.recordTitle || '').trim(),
recordContent: trimOrUndefined(source.recordContent),
recordDate: trimOrUndefined(source.recordDate),
remindTime: trimOrUndefined(source.remindTime),
mediaOssIds: trimOrUndefined(source.mediaOssIds),
sortOrder: toNumber(source.sortOrder, 1),
status: String(source.status || '').trim() || '0'
};
}
function getRecordId(item) {
return pick(item, ['recordId', 'id'], '');
}
function buildGrowthRow(item, genealogyId) {
var recordId = getRecordId(item);
var title = pick(item, ['recordTitle', 'title'], '未命名成长记录');
var type = pick(item, ['recordType', 'type'], 'growth');
var date = pick(item, ['recordDate', 'createTime'], '未记录日期');
return '<a class="module-row growth-row" href="profile-growth-edit.html?recordId=' + encodeURIComponent(recordId) + '&genealogyId=' + encodeURIComponent(genealogyId || '') + '">' +
'<div><h3>' + escapeHtml(title) + '</h3><p>' + escapeHtml(type) + ' · ' + escapeHtml(date) + '</p></div>' +
'<span class="pill">编辑</span></a>';
}
function renderGrowthRecords(items, genealogyId) {
var container = query('[data-growth-list]');
var list = normalizeList(items);
if (!container) return;
if (!list.length) {
container.innerHTML = '<div class="api-empty">暂无成长记录</div>';
return;
}
container.innerHTML = list.map(function (item) {
return buildGrowthRow(item, genealogyId);
}).join('');
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
values[field.name] = field.value;
});
return values;
}
function fillGrowthForm(record) {
var source = record || {};
queryAll('[name]').forEach(function (field) {
var value = pick(source, [field.name], '');
if (value !== '') field.value = value;
});
}
async function loadGrowthRecords() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var recordId = getCurrentRecordId();
if (!api || !genealogyId) return;
try {
if (query('[data-growth-list]')) {
renderGrowthRecords(await api.growthRecords(genealogyId), genealogyId);
}
if (recordId && query('[data-growth-edit-page]')) {
fillGrowthForm(await api.growthRecordDetail(genealogyId, recordId));
}
} catch (error) {
showMessage(error.message || '成长记录加载失败');
}
}
async function submitGrowth(form) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var recordId = getCurrentRecordId();
var body = buildGrowthBody(getFormValues(form));
if (!api || !genealogyId) return;
if (!body.recordTitle) {
showMessage('请填写成长记录标题');
return;
}
try {
if (recordId) {
await api.updateGrowthRecord(genealogyId, recordId, body);
} else {
await api.createGrowthRecord(genealogyId, body);
}
showMessage('成长记录已保存');
root.location.href = 'profile-growth.html?genealogyId=' + encodeURIComponent(genealogyId);
} catch (error) {
showMessage(error.message || '保存成长记录失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('submit', function (event) {
var form = event.target.closest('[data-growth-form]');
if (!form) return;
event.preventDefault();
submitGrowth(form);
});
}
function init() {
if (!documentRef) return;
bindActions();
loadGrowthRecords();
}
return {
normalizeList: normalizeList,
buildGrowthBody: buildGrowthBody,
buildGrowthRow: buildGrowthRow,
init: init
};
});
+156
View File
@@ -0,0 +1,156 @@
(function (root, factory) {
// 帮助中心模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.HelpPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.HelpPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 通用列表响应可能是数组,也可能包在 rows、records、list 或 data 中。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 帮助文章来自接口,进入 innerHTML 前先转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function buildHelpItem(item, index) {
// 列表接口字段未在文档中细化,这里兼容常见标题和摘要字段。
var id = pick(item, ['helpId', 'id', 'articleId'], '');
var title = pick(item, ['title', 'articleTitle', 'name'], '帮助文章');
var content = pick(item, ['summary', 'description', 'content', 'articleContent'], '暂无详细说明');
var hasFullContent = Boolean(pick(item, ['content', 'articleContent'], ''));
var open = index === 0 ? ' open' : '';
return '<details' + open + ' class="tilt-card" data-help-id="' + escapeHtml(id) + '" data-help-loaded="' + (hasFullContent ? 'true' : 'false') + '">' +
'<summary>' + escapeHtml(title) + '</summary>' +
'<p>' + escapeHtml(content) + '</p>' +
'</details>';
}
function renderHelpArticles(items) {
var container = query('[data-help-list]');
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">暂无帮助文章</div>';
return;
}
container.innerHTML = items.map(buildHelpItem).join('');
}
async function loadHelpArticles() {
var api = getApi();
var container = query('[data-help-list]');
if (!api || !container) return;
container.classList.add('is-loading');
try {
renderHelpArticles(normalizeList(await api.helpArticles()));
} catch (error) {
showMessage(error.message || '帮助文章加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function loadHelpDetail(details) {
var api = getApi();
var helpId = details && details.getAttribute('data-help-id');
var paragraph = details && query('p', details);
var content;
if (!api || !details || !helpId || details.getAttribute('data-help-loaded') === 'true') return;
details.classList.add('is-loading');
try {
content = await api.helpArticleDetail(helpId);
paragraph.textContent = pick(content, ['content', 'articleContent', 'summary', 'description'], paragraph.textContent);
details.setAttribute('data-help-loaded', 'true');
} catch (error) {
showMessage(error.message || '帮助详情加载失败');
} finally {
details.classList.remove('is-loading');
}
}
function bindDetailLoad() {
var container = query('[data-help-list]');
if (!container) return;
container.addEventListener('toggle', function (event) {
if (event.target && event.target.matches('details') && event.target.open) {
loadHelpDetail(event.target);
}
}, true);
}
function init() {
if (!documentRef) return;
bindDetailLoad();
loadHelpArticles();
}
return {
normalizeList: normalizeList,
buildHelpItem: buildHelpItem,
renderHelpArticles: renderHelpArticles,
init: init
};
});
+271
View File
@@ -0,0 +1,271 @@
(function (root, factory) {
// 加入申请模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.JoinApplyPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.JoinApplyPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 列表响应可能直接是数组,也可能使用 rows、records、list 或 data 包裹。
if (Array.isArray(data)) return data;
if (!data || typeof data !== 'object') return [];
if (Array.isArray(data.rows)) return data.rows;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
if (Array.isArray(data.data)) return data.data;
return [];
}
function pick(item, keys, fallback) {
var source = item || {};
var index;
var value;
for (index = 0; index < keys.length; index += 1) {
value = source[keys[index]];
if (value !== undefined && value !== null && value !== '') return value;
}
return fallback || '';
}
function escapeHtml(value) {
// 申请人信息来自接口,渲染前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
return (rootNode || documentRef).querySelector(selector);
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
root.alert(message);
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId() {
var search = root.location && root.location.search;
var fromQuery = getQueryParam(search, 'id') || getQueryParam(search, 'genealogyId');
var fromPage = query('[data-genealogy-id]');
return fromQuery || (fromPage && fromPage.getAttribute('data-genealogy-id')) || '';
}
function formatApplyStatus(status) {
// 审核接口要求 status,按常见字典值做页面中文展示。
var map = {
0: '待审核',
1: '已通过',
2: '已拒绝'
};
return map[status] || status || '待审核';
}
function buildAuditBody(action, remark) {
var isReject = action === 'reject';
return {
status: isReject ? '2' : '1',
auditRemark: remark || (isReject ? '信息核验未通过' : '信息核验通过')
};
}
function buildPendingRow(item, genealogyId) {
var applyId = pick(item, ['applyId', 'id'], '');
var name = pick(item, ['applicantName', 'name', 'nickName'], '未命名申请人');
var relation = pick(item, ['relationDesc', 'applyReason', 'remark'], '关系说明待确认');
var time = pick(item, ['createTime', 'createdAt', 'applyTime'], '提交时间待确认');
return '<div class="module-row join-apply-row" data-join-apply-id="' + escapeHtml(applyId) + '">' +
'<div><h3>' + escapeHtml(name) + '申请加入家谱</h3><p>' + escapeHtml(time) + ' · ' + escapeHtml(relation) + '</p></div>' +
'<div class="bottom-actions">' +
'<button class="btn primary" type="button" data-join-audit="approve" data-genealogy-id="' + escapeHtml(genealogyId) + '" data-apply-id="' + escapeHtml(applyId) + '">同意</button>' +
'<button class="btn ghost" type="button" data-join-audit="reject" data-genealogy-id="' + escapeHtml(genealogyId) + '" data-apply-id="' + escapeHtml(applyId) + '">拒绝</button>' +
'</div></div>';
}
function buildMineRow(item) {
var applyId = pick(item, ['applyId', 'id'], '');
var genealogyName = pick(item, ['genealogyName', 'familyName', 'name'], '未命名家谱');
var status = formatApplyStatus(pick(item, ['status', 'auditStatus'], '0'));
var relation = pick(item, ['relationDesc', 'applyReason', 'auditRemark'], '关系说明待确认');
var action = status === '待审核'
? '<button class="btn ghost" type="button" data-join-cancel="' + escapeHtml(applyId) + '">撤销申请</button>'
: '<span class="pill">' + escapeHtml(status) + '</span>';
return '<div class="module-row join-apply-row" data-join-apply-id="' + escapeHtml(applyId) + '">' +
'<div><h3>' + escapeHtml(genealogyName) + '</h3><p>' + escapeHtml(status) + ' · ' + escapeHtml(relation) + '</p></div>' +
action +
'</div>';
}
function renderPending(items, genealogyId) {
var container = query('[data-join-apply-list="pending"]');
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">暂无待审核申请</div>';
return;
}
container.innerHTML = items.map(function (item) {
return buildPendingRow(item, genealogyId);
}).join('');
}
function renderMine(items) {
var container = query('[data-join-apply-list="mine"]');
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">暂无加入申请记录</div>';
return;
}
container.innerHTML = items.map(buildMineRow).join('');
}
async function loadPending() {
var api = getApi();
var container = query('[data-join-apply-list="pending"]');
var genealogyId = getCurrentGenealogyId();
if (!api || !container) return;
if (!genealogyId) {
container.innerHTML = '<div class="api-empty">请从具体家谱进入审核页</div>';
return;
}
container.classList.add('is-loading');
try {
renderPending(normalizeList(await api.pendingJoinApplies(genealogyId)), genealogyId);
} catch (error) {
showMessage(error.message || '待审核申请加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function loadMine() {
var api = getApi();
var container = query('[data-join-apply-list="mine"]');
if (!api || !container) return;
container.classList.add('is-loading');
try {
renderMine(normalizeList(await api.myJoinApplies()));
} catch (error) {
showMessage(error.message || '加入申请记录加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function audit(button) {
var api = getApi();
var action = button.getAttribute('data-join-audit');
var genealogyId = button.getAttribute('data-genealogy-id');
var applyId = button.getAttribute('data-apply-id');
var remark = action === 'reject' ? root.prompt('请输入拒绝原因', '') : '';
if (!api || !genealogyId || !applyId) return;
if (action === 'reject' && remark === null) return;
button.classList.add('is-loading');
try {
await api.auditJoinApply(genealogyId, applyId, buildAuditBody(action, remark));
showMessage(action === 'reject' ? '已拒绝申请' : '已同意申请');
loadPending();
} catch (error) {
showMessage(error.message || '审核申请失败');
} finally {
button.classList.remove('is-loading');
}
}
async function cancel(button) {
var api = getApi();
var applyId = button.getAttribute('data-join-cancel');
if (!api || !applyId) return;
if (root.confirm && !root.confirm('确认撤销当前加入申请?')) return;
button.classList.add('is-loading');
try {
await api.cancelJoinApply(applyId);
showMessage('已撤销加入申请');
loadMine();
} catch (error) {
showMessage(error.message || '撤销申请失败');
} finally {
button.classList.remove('is-loading');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('click', function (event) {
var auditButton = event.target.closest('[data-join-audit]');
var cancelButton = event.target.closest('[data-join-cancel]');
if (auditButton) {
event.preventDefault();
audit(auditButton);
}
if (cancelButton) {
event.preventDefault();
cancel(cancelButton);
}
});
}
function init() {
if (!documentRef) return;
bindActions();
loadPending();
loadMine();
}
return {
normalizeList: normalizeList,
formatApplyStatus: formatApplyStatus,
buildAuditBody: buildAuditBody,
buildPendingRow: buildPendingRow,
buildMineRow: buildMineRow,
init: init
};
});
+2
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

+948
View File
@@ -0,0 +1,948 @@
/* common */
.ke-inline-block {
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
zoom: 1;
*display: inline;
}
.ke-clearfix {
zoom: 1;
}
.ke-clearfix:after {
content: ".";
display: block;
clear: both;
font-size: 0;
height: 0;
line-height: 0;
visibility: hidden;
}
.ke-shadow {
box-shadow: 1px 1px 3px #A0A0A0;
-moz-box-shadow: 1px 1px 3px #A0A0A0;
-webkit-box-shadow: 1px 1px 3px #A0A0A0;
filter: progid:DXImageTransform.Microsoft.Shadow(color='#A0A0A0', Direction=135, Strength=3);
background-color: #F0F0EE;
}
.ke-menu a,
.ke-menu a:hover,
.ke-dialog a,
.ke-dialog a:hover {
color: #337FE5;
text-decoration: none;
}
/* icons */
.ke-icon-source {
background-position: 0px 0px;
width: 16px;
height: 16px;
}
.ke-icon-preview {
background-position: 0px -16px;
width: 16px;
height: 16px;
}
.ke-icon-print {
background-position: 0px -32px;
width: 16px;
height: 16px;
}
.ke-icon-undo {
background-position: 0px -48px;
width: 16px;
height: 16px;
}
.ke-icon-redo {
background-position: 0px -64px;
width: 16px;
height: 16px;
}
.ke-icon-cut {
background-position: 0px -80px;
width: 16px;
height: 16px;
}
.ke-icon-copy {
background-position: 0px -96px;
width: 16px;
height: 16px;
}
.ke-icon-paste {
background-position: 0px -112px;
width: 16px;
height: 16px;
}
.ke-icon-selectall {
background-position: 0px -128px;
width: 16px;
height: 16px;
}
.ke-icon-justifyleft {
background-position: 0px -144px;
width: 16px;
height: 16px;
}
.ke-icon-justifycenter {
background-position: 0px -160px;
width: 16px;
height: 16px;
}
.ke-icon-justifyright {
background-position: 0px -176px;
width: 16px;
height: 16px;
}
.ke-icon-justifyfull {
background-position: 0px -192px;
width: 16px;
height: 16px;
}
.ke-icon-insertorderedlist {
background-position: 0px -208px;
width: 16px;
height: 16px;
}
.ke-icon-insertunorderedlist {
background-position: 0px -224px;
width: 16px;
height: 16px;
}
.ke-icon-indent {
background-position: 0px -240px;
width: 16px;
height: 16px;
}
.ke-icon-outdent {
background-position: 0px -256px;
width: 16px;
height: 16px;
}
.ke-icon-subscript {
background-position: 0px -272px;
width: 16px;
height: 16px;
}
.ke-icon-superscript {
background-position: 0px -288px;
width: 16px;
height: 16px;
}
.ke-icon-date {
background-position: 0px -304px;
width: 25px;
height: 16px;
}
.ke-icon-time {
background-position: 0px -320px;
width: 25px;
height: 16px;
}
.ke-icon-formatblock {
background-position: 0px -336px;
width: 25px;
height: 16px;
}
.ke-icon-fontname {
background-position: 0px -352px;
width: 21px;
height: 16px;
}
.ke-icon-fontsize {
background-position: 0px -368px;
width: 23px;
height: 16px;
}
.ke-icon-forecolor {
background-position: 0px -384px;
width: 20px;
height: 16px;
}
.ke-icon-hilitecolor {
background-position: 0px -400px;
width: 23px;
height: 16px;
}
.ke-icon-bold {
background-position: 0px -416px;
width: 16px;
height: 16px;
}
.ke-icon-italic {
background-position: 0px -432px;
width: 16px;
height: 16px;
}
.ke-icon-underline {
background-position: 0px -448px;
width: 16px;
height: 16px;
}
.ke-icon-strikethrough {
background-position: 0px -464px;
width: 16px;
height: 16px;
}
.ke-icon-removeformat {
background-position: 0px -480px;
width: 16px;
height: 16px;
}
.ke-icon-image {
background-position: 0px -496px;
width: 16px;
height: 16px;
}
.ke-icon-flash {
background-position: 0px -512px;
width: 16px;
height: 16px;
}
.ke-icon-media {
background-position: 0px -528px;
width: 16px;
height: 16px;
}
.ke-icon-div {
background-position: 0px -544px;
width: 16px;
height: 16px;
}
.ke-icon-formula {
background-position: 0px -576px;
width: 16px;
height: 16px;
}
.ke-icon-hr {
background-position: 0px -592px;
width: 16px;
height: 16px;
}
.ke-icon-emoticons {
background-position: 0px -608px;
width: 16px;
height: 16px;
}
.ke-icon-link {
background-position: 0px -624px;
width: 16px;
height: 16px;
}
.ke-icon-unlink {
background-position: 0px -640px;
width: 16px;
height: 16px;
}
.ke-icon-fullscreen {
background-position: 0px -656px;
width: 16px;
height: 16px;
}
.ke-icon-about {
background-position: 0px -672px;
width: 16px;
height: 16px;
}
.ke-icon-plainpaste {
background-position: 0px -704px;
width: 16px;
height: 16px;
}
.ke-icon-wordpaste {
background-position: 0px -720px;
width: 16px;
height: 16px;
}
.ke-icon-table {
background-position: 0px -784px;
width: 16px;
height: 16px;
}
.ke-icon-tablemenu {
background-position: 0px -768px;
width: 16px;
height: 16px;
}
.ke-icon-tableinsert {
background-position: 0px -784px;
width: 16px;
height: 16px;
}
.ke-icon-tabledelete {
background-position: 0px -800px;
width: 16px;
height: 16px;
}
.ke-icon-tablecolinsertleft {
background-position: 0px -816px;
width: 16px;
height: 16px;
}
.ke-icon-tablecolinsertright {
background-position: 0px -832px;
width: 16px;
height: 16px;
}
.ke-icon-tablerowinsertabove {
background-position: 0px -848px;
width: 16px;
height: 16px;
}
.ke-icon-tablerowinsertbelow {
background-position: 0px -864px;
width: 16px;
height: 16px;
}
.ke-icon-tablecoldelete {
background-position: 0px -880px;
width: 16px;
height: 16px;
}
.ke-icon-tablerowdelete {
background-position: 0px -896px;
width: 16px;
height: 16px;
}
.ke-icon-tablecellprop {
background-position: 0px -912px;
width: 16px;
height: 16px;
}
.ke-icon-tableprop {
background-position: 0px -928px;
width: 16px;
height: 16px;
}
.ke-icon-checked {
background-position: 0px -944px;
width: 16px;
height: 16px;
}
.ke-icon-code {
background-position: 0px -960px;
width: 16px;
height: 16px;
}
.ke-icon-map {
background-position: 0px -976px;
width: 16px;
height: 16px;
}
.ke-icon-baidumap {
background-position: 0px -976px;
width: 16px;
height: 16px;
}
.ke-icon-lineheight {
background-position: 0px -992px;
width: 16px;
height: 16px;
}
.ke-icon-clearhtml {
background-position: 0px -1008px;
width: 16px;
height: 16px;
}
.ke-icon-pagebreak {
background-position: 0px -1024px;
width: 16px;
height: 16px;
}
.ke-icon-insertfile {
background-position: 0px -1040px;
width: 16px;
height: 16px;
}
.ke-icon-quickformat {
background-position: 0px -1056px;
width: 16px;
height: 16px;
}
.ke-icon-template {
background-position: 0px -1072px;
width: 16px;
height: 16px;
}
.ke-icon-tablecellsplit {
background-position: 0px -1088px;
width: 16px;
height: 16px;
}
.ke-icon-tablerowmerge {
background-position: 0px -1104px;
width: 16px;
height: 16px;
}
.ke-icon-tablerowsplit {
background-position: 0px -1120px;
width: 16px;
height: 16px;
}
.ke-icon-tablecolmerge {
background-position: 0px -1136px;
width: 16px;
height: 16px;
}
.ke-icon-tablecolsplit {
background-position: 0px -1152px;
width: 16px;
height: 16px;
}
.ke-icon-anchor {
background-position: 0px -1168px;
width: 16px;
height: 16px;
}
.ke-icon-search {
background-position: 0px -1184px;
width: 16px;
height: 16px;
}
.ke-icon-new {
background-position: 0px -1200px;
width: 16px;
height: 16px;
}
.ke-icon-specialchar {
background-position: 0px -1216px;
width: 16px;
height: 16px;
}
.ke-icon-multiimage {
background-position: 0px -1232px;
width: 16px;
height: 16px;
}
.ke-icon-zijianju {
background-position: 0px -1248px;
width: 16px;
height: 16px;
}
.ke-icon-duansuo {
background-position: 0px -1264px;
width: 16px;
height: 16px;
}
/* container */
.ke-container {
display: block;
/*border: 1px solid #CCCCCC;*/
background-color: #FFF;
overflow: hidden;
padding: 0;
}
/* toolbar */
.ke-toolbar {
border-bottom: 1px solid #CCC;
background-color: #F0F0EE;
text-align: left;
overflow: hidden;
zoom: 1;
}
.ke-toolbar-icon {
background-repeat: no-repeat;
font-size: 0;
line-height: 0;
overflow: hidden;
display: block;
}
.ke-toolbar-icon-url {
background-image: url(default.png);
}
.ke-toolbar .ke-outline {
border: 1px solid #F0F0EE;
/*margin: 1px;*/
margin: 8px 9px;
padding: 1px 2px;
font-size: 0;
line-height: 0;
overflow: hidden;
cursor: pointer;
display: block;
float: left;
}
.ke-toolbar .ke-on {
border: 1px solid #5690D2;
}
.ke-toolbar .ke-selected {
border: 1px solid #5690D2;
background-color: #E9EFF6;
}
.ke-toolbar .ke-disabled {
cursor: default;
}
.ke-toolbar .ke-separator {
height: 16px;
margin: 2px 3px;
border-left: 1px solid #A0A0A0;
border-right: 1px solid #FFFFFF;
border-top:0;
border-bottom:0;
width: 0;
font-size: 0;
line-height: 0;
overflow: hidden;
display: block;
float: left;
}
.ke-toolbar .ke-hr {
overflow: hidden;
height: 1px;
clear: both;
}
/* edit */
.ke-edit {
padding: 0;
}
.ke-edit-iframe,
.ke-edit-textarea {
border: 0;
margin: 0;
padding: 0;
overflow: auto;
}
.ke-edit-textarea {
font: 12px/1.5 "Consolas", "Monaco", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
color: #000;
overflow: auto;
resize: none;
}
.ke-edit-textarea:focus {
outline: none;
}
/* statusbar */
.ke-statusbar {
position: relative;
background-color: #F0F0EE;
border-top: 1px solid #CCCCCC;
font-size: 0;
line-height: 0;
*height: 12px;
overflow: hidden;
text-align: center;
cursor: s-resize;
}
.ke-statusbar-center-icon {
background-position: -0px -754px;
width: 15px;
height: 11px;
background-image: url(default.png);
}
.ke-statusbar-right-icon {
position: absolute;
right: 0;
bottom: 0;
cursor: se-resize;
background-position: -5px -741px;
width: 11px;
height: 11px;
background-image: url(default.png);
}
/* menu */
.ke-menu {
border: 1px solid #A0A0A0;
background-color: #F1F1F1;
color: #222222;
padding: 2px;
font-family: "sans serif",tahoma,verdana,helvetica;
font-size: 12px;
text-align: left;
overflow: hidden;
}
.ke-menu-item {
border: 1px solid #F1F1F1;
background-color: #F1F1F1;
color: #222222;
height: 24px;
overflow: hidden;
cursor: pointer;
}
.ke-menu-item-on {
border: 1px solid #5690D2;
background-color: #E9EFF6;
}
.ke-menu-item-left {
width: 27px;
text-align: center;
overflow: hidden;
}
.ke-menu-item-center {
width: 0;
height: 24px;
border-left: 1px solid #E3E3E3;
border-right: 1px solid #FFFFFF;
border-top: 0;
border-bottom: 0;
}
.ke-menu-item-center-on {
border-left: 1px solid #E9EFF6;
border-right: 1px solid #E9EFF6;
}
.ke-menu-item-right {
border: 0;
padding: 0 0 0 5px;
line-height: 24px;
text-align: left;
overflow: hidden;
}
.ke-menu-separator {
margin: 2px 0;
height: 0;
overflow: hidden;
border-top: 1px solid #CCCCCC;
border-bottom: 1px solid #FFFFFF;
border-left: 0;
border-right: 0;
}
/* colorpicker */
.ke-colorpicker {
border: 1px solid #A0A0A0;
background-color: #F1F1F1;
color: #222222;
padding: 2px;
}
.ke-colorpicker-table {
border:0;
margin:0;
padding:0;
border-collapse: separate;
}
.ke-colorpicker-cell {
font-size: 0;
line-height: 0;
border: 1px solid #F0F0EE;
cursor: pointer;
margin:3px;
padding:0;
}
.ke-colorpicker-cell-top {
font-family: "sans serif",tahoma,verdana,helvetica;
font-size: 12px;
line-height: 24px;
border: 1px solid #F0F0EE;
cursor: pointer;
margin:0;
padding:0;
text-align: center;
}
.ke-colorpicker-cell-on {
border: 1px solid #5690D2;
}
.ke-colorpicker-cell-selected {
border: 1px solid #2446AB;
}
.ke-colorpicker-cell-color {
width: 14px;
height: 14px;
margin: 3px;
padding: 0;
border: 0;
}
/* dialog */
.ke-dialog {
position: absolute;
margin: 0;
padding: 0;
}
.ke-dialog .ke-header {
width: 100%;
margin-bottom: 10px;
}
.ke-dialog .ke-header .ke-left {
float: left;
}
.ke-dialog .ke-header .ke-right {
float: right;
}
.ke-dialog .ke-header label {
margin-right: 0;
cursor: pointer;
font-weight: normal;
display: inline;
vertical-align: top;
}
.ke-dialog-content {
background-color: #FFF;
width: 100%;
height: 100%;
color: #333;
border: 1px solid #A0A0A0;
}
.ke-dialog-shadow {
position: absolute;
z-index: -1;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 3px 3px 7px #999;
-moz-box-shadow: 3px 3px 7px #999;
-webkit-box-shadow: 3px 3px 7px #999;
filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='3', MakeShadow='true', ShadowOpacity='0.4');
background-color: #F0F0EE;
}
.ke-dialog-header {
border:0;
margin:0;
padding: 0 10px;
background: url(background.png) repeat scroll 0 0 #F0F0EE;
border-bottom: 1px solid #CFCFCF;
height: 24px;
font: 12px/24px "sans serif",tahoma,verdana,helvetica;
text-align: left;
color: #222;
cursor: move;
}
.ke-dialog-icon-close {
display: block;
background: url(default.png) no-repeat scroll 0px -688px;
width: 16px;
height: 16px;
position: absolute;
right: 6px;
top: 6px;
cursor: pointer;
}
.ke-dialog-body {
font: 12px/1.5 "sans serif",tahoma,verdana,helvetica;
text-align: left;
overflow: hidden;
width: 100%;
}
.ke-dialog-body textarea {
display: block;
overflow: auto;
padding: 0;
resize: none;
}
.ke-dialog-body textarea:focus,
.ke-dialog-body input:focus,
.ke-dialog-body select:focus {
outline: none;
}
.ke-dialog-body label {
margin-right: 10px;
cursor: pointer;
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
zoom: 1;
*display: inline;
}
.ke-dialog-body img {
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
zoom: 1;
*display: inline;
}
.ke-dialog-body select {
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
zoom: 1;
*display: inline;
width: auto;
}
.ke-dialog-body .ke-textarea {
display: block;
width: 408px;
height: 260px;
font-family: "sans serif",tahoma,verdana,helvetica;
font-size: 12px;
border-color: #848484 #E0E0E0 #E0E0E0 #848484;
border-style: solid;
border-width: 1px;
}
.ke-dialog-body .ke-form {
margin: 0;
padding: 0;
}
.ke-dialog-loading {
position: absolute;
top: 0;
left: 1px;
z-index: 1;
text-align: center;
}
.ke-dialog-loading-content {
background: url("../common/loading.gif") no-repeat;
color: #666;
font-size: 14px;
font-weight: bold;
height: 31px;
line-height: 31px;
padding-left: 36px;
}
.ke-dialog-row {
margin-bottom: 10px;
}
.ke-dialog-footer {
font: 12px/1 "sans serif",tahoma,verdana,helvetica;
text-align: right;
padding:0 0 5px 0;
background-color: #FFF;
width: 100%;
}
.ke-dialog-preview,
.ke-dialog-yes {
margin: 5px;
}
.ke-dialog-no {
margin: 5px 10px 5px 5px;
}
.ke-dialog-mask {
background-color:#FFF;
filter:alpha(opacity=50);
opacity:0.5;
}
.ke-button-common {
background: url(background.png) no-repeat;
cursor: pointer;
height: 23px;
line-height: 23px;
overflow: visible;
display: inline-block;
vertical-align: top;
cursor: pointer;
}
.ke-button-outer {
background-position: 0 -25px;
padding: 0;
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
zoom: 1;
*display: inline;
}
.ke-button {
background-position: right -25px;
padding: 0 14px 0 12px;
margin: 0 0 0 2px;
font-family: "sans serif",tahoma,verdana,helvetica;
border: 0 none;
color: #333;
font-size: 12px;
text-decoration: none;
}
/* inputbox */
.ke-input-text {
background-color:#FFFFFF;
font-family: "sans serif",tahoma,verdana,helvetica;
font-size: 12px;
line-height: 17px;
height: 17px;
padding: 2px 4px;
border-color: #848484 #E0E0E0 #E0E0E0 #848484;
border-style: solid;
border-width: 1px;
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
zoom: 1;
*display: inline;
}
.ke-input-number {
width: 50px;
}
.ke-input-color {
border: 1px solid #A0A0A0;
background-color: #FFFFFF;
font-size: 12px;
width: 60px;
height: 20px;
line-height: 20px;
padding-left: 5px;
overflow: hidden;
cursor: pointer;
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
zoom: 1;
*display: inline;
}
.ke-upload-button {
position: relative;
}
.ke-upload-area {
position: relative;
overflow: hidden;
margin: 0;
padding: 0;
*height: 25px;
}
.ke-upload-area .ke-upload-file {
position: absolute;
font-size: 60px;
top: 0;
right: 0;
padding: 0;
margin: 0;
z-index: 811212;
border: 0 none;
opacity: 0;
filter: alpha(opacity=0);
}
/* tabs */
.ke-tabs {
font: 12px/1 "sans serif",tahoma,verdana,helvetica;
border-bottom:1px solid #A0A0A0;
padding-left:5px;
margin-bottom:20px;
}
.ke-tabs-ul {
list-style-image:none;
list-style-position:outside;
list-style-type:none;
margin:0;
padding:0;
}
.ke-tabs-li {
position: relative;
border: 1px solid #A0A0A0;
background-color: #F0F0EE;
margin: 0 2px -1px 0;
padding: 0 20px;
float: left;
line-height: 25px;
text-align: center;
color: #555555;
cursor: pointer;
}
.ke-tabs-li-selected {
background-color: #FFF;
border-bottom: 1px solid #FFF;
color: #000;
cursor: default;
}
.ke-tabs-li-on {
background-color: #FFF;
color: #000;
}
/* progressbar */
.ke-progressbar {
position: relative;
margin: 0;
padding: 0;
}
.ke-progressbar-bar {
border: 1px solid #6FA5DB;
width: 80px;
height: 5px;
margin: 10px 10px 0 10px;
padding: 0;
}
.ke-progressbar-bar-inner {
width: 0;
height: 5px;
background-color: #6FA5DB;
overflow: hidden;
margin: 0;
padding: 0;
}
.ke-progressbar-percent {
position: absolute;
top: 0;
left: 40%;
display: none;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+58
View File
@@ -0,0 +1,58 @@
/**
* date:2019/08/16
* author:Mr.Chung
* description:此处放layui自定义扩展
* version:2.0.4
*/
window.rootPath = (function (src) {
src = document.scripts[document.scripts.length - 1].src;
return src.substring(0, src.lastIndexOf("/") + 1);
})();
layui.config({
base: rootPath + "lay-module/",
version: true
}).extend({
xphp: "layuimini/xphp", // layuimini后台扩展
miniAdmin: "layuimini/miniAdmin", // layuimini后台扩展
miniMenu: "layuimini/miniMenu", // layuimini菜单扩展
miniTab: "layuimini/miniTab", // layuimini tab扩展
miniTheme: "layuimini/miniTheme", // layuimini 主题扩展
step: 'step-lay/step', // 分步表单扩展
treetable: 'treetable-lay/treetable', //table树形扩展
tableSelect: 'tableSelect/tableSelect', // table选择扩展
echarts: 'echarts/echarts', // echarts图表扩展
echartsTheme: 'echarts/echartsTheme', // echarts图表主题扩展
wangEditor: 'wangEditor/wangEditor', // wangEditor富文本扩展
layarea: 'layarea/layarea', // 省市县区三级联动下拉选择器
selectM: 'selectM/selectM',//下拉多选
selectN: 'selectN/selectN',//多个select循环无限下级选
numinput: 'numinput/numinput',//数字输入
regionCheckBox: 'regionCheckBox/regionCheckBox',//省市县选择
openTable: 'openTable/openTable',//用途不大
iPicker: 'ipicker/iPicker',//多个panel无限循环下级多选
layselect: 'layselect/layselect',//下拉单选,支持联动
selectY: 'selectY/selectY',//只需要pid,id,name,就可以无限级选择
selectY2: 'selectY/selectY2',//只需要pid,id,name,就可以无限级选择
tableFilter: 'tablefilter/tableFilter',//列数值筛选
sliderVerify: 'sliderVerify/sliderVerify',//滑动验证
cascader: 'cascader/cascader',//级联选择组件
checkbox: 'checkbox/checkbox',//多选美化
dropdownTable: 'dropdownTable/dropdownTable',//下拉表格选择组件
hashes: 'hashes/hashes',//hash:md5 sha1 sha256 sha512 crc32
inputTag: 'inputTag/inputTag',//标签输入
jmSheet: 'jmSheet/jmSheet',//底部弹出sheet
location: 'location/location',//百度地图经纬度
locationX: 'location/locationX',//百度地图经纬度x
tableMerge: 'tableMerge/tableMerge',//表格列合并
textool: 'textool/textool.min',//文本输入工具
moreMenus: 'moreMenus/moreMenus',//更多/隐藏
sliderTime: 'sliderTime/sliderTime',//时间范围选择
tableEdit: 'tableTree/tableEdit',//表格编辑
tableTree: 'tableTree/tableTree',//表格树
transferTable: 'transferTable/transferTable',//穿梭
notice: "notice/notice", // layuimini后台扩展
mods: "mods/mods", // jsannotice
passwordIntensity: "passwordIntensity/passwordIntensity", // jsannotice
});
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
.checkBox .block{float:left; margin:5px;padding:6px 15px;height:18px;text-align:center; border:1px solid #ccc;position:relative;cursor:pointer;overflow:hidden;}
.checkBox .block .choice{position:absolute;right:0px;bottom:0px;display:none;}
.checkBox .block.on{border:1px solid #1E9FFF;}
.checkBox .block.on .choice{position:absolute;right:0px;bottom:0px;display:block;}
.checkBox .block.on .choice .triangle{position:absolute;right:0px;bottom:0px;border-bottom:18px solid #1E9FFF;border-left: 18px solid transparent;}
.checkBox .block.on .choice .right{position:absolute;right:5px;bottom:5px;width:10px;height:10px;}
.checkBox .block .del{width:18px; height:18px; background:#fff; border:0px solid #ccc; border-radius:3px; overflow:hidden; cursor:pointer; position:absolute; bottom:-20px; right:0px; transition: background .3s ease, border .2s ease, bottom .2s ease;display:block;}
.checkBox .block .del:hover{background:#FF5722; border:1px solid #FF5722;}
.checkBox .block:hover .del{bottom:0px;}
.checkBox .block.on .del{display:none}
+69
View File
@@ -0,0 +1,69 @@
layui.define('jquery', function(exports){
"use strict";
var $ = layui.$
,hint = layui.hint();
var CheckBox = function(options){
this.options = options;
};
//初始化
CheckBox.prototype.init = function(elem){
var that = this;
elem.addClass('checkBox'); //添加checkBox样式
that.checkbox(elem);
};
//树节点解析
CheckBox.prototype.checkbox = function(elem,children){
var that = this, options = that.options;
var nodes = children || options.nodes;
layui.each(nodes, function(index, item){
var li = $(['<li class="block'+(item.on?' on':'')+'" value="'+item.name+'" onmouseover="layui.layer.tips(\''+item.type+'\',this,{tips:2})" onmouseout="layui.layer.closeAll(\'tips\');">'+item.name,'<i class="choice"><i class="triangle"></i><i class="right layui-icon layui-icon-ok"></i></i><i class="del"><i class="layui-icon layui-icon-delete"></i></i><span class="hide">'+(item.on?'<input type="hidden" name="'+item.name+'" value="'+item.type+'">':'')+'</span></li>'].join(''));
elem.append(li);
//触发点击节点回调
typeof options.click === 'function' && that.click(li, item);
//触发删除节点回调
typeof options.del === 'function' && that.del(li, item);
});
};
//点击节点回调
CheckBox.prototype.click = function(elem, item){
var that = this, options = that.options;
elem.on('click', function(e){
elem.toggleClass("on");
if(elem.hasClass("on")){
item.on = true;
elem.children("span.hide").html('<input type="hidden" name="'+item.name+'" value="'+item.type+'">');
}else{
item.on = false;
elem.children("span.hide").html('');
}
layui.stope(e);
options.click(item);
});
};
//点击节点回调
CheckBox.prototype.del = function(elem, item){
var that = this, options = that.options;
elem.children('i.del').on('click', function(e){
var index = layer.confirm('确定删除 ['+item.name+'] 吗?', {
btn: ['删除','取消']
}, function(){
layer.close(index);
if(options.del(item)){
elem.closest(".block").remove();
layui.stope(e);
}
});
return false;
});
};
//暴露接口
exports('checkbox', function(options){
var checkbox = new CheckBox(options = options || {});
var elem = $(options.elem);
if(!elem[0]){
return hint.error('layui.checkbox 没有找到'+ options.elem +'元素');
}
checkbox.init(elem);
});
}).link(layui.cache.base+"checkbox/checkbox.css?v="+(new Date).getTime());
@@ -0,0 +1,677 @@
layui.define(['jquery', 'dropdown', 'table', 'form'], function (exports) {
"use strict";
var dropdown = layui.dropdown, //下拉菜单
table = layui.table, //table组件
$ = layui.jquery, //jQuery
form = layui.form, //表单
moduleName = 'dropdownTable', //模块名
dropdownTable = {
version: '1.0.4',
config: {
selectType: 'radio',
emptyMsg: '点击此处选择'
},
index: layui[moduleName] ? (layui[moduleName].index + 10000) : 0,
set: function (options) {
this.config = $.extend(true, {}, that.config, options);
let config = this.config,
selectType = config.selectType,
selectField = { type: selectType, fixed: 'left' },
cols = config?.selectTable?.cols ?? [[]];
cols.forEach((item) => {
if (JSON.stringify(item).indexOf(JSON.stringify(selectField)) < 0) {
item.splice(0, 0, selectField);
}
});
return that;
}
},
//操作当前实例
thisModule = function () {
let that = this,
options = that.config,
id = options.id || that.index;
thisModule.that[id] = that; //记录当前实例对象
return {
config: options,
reload: function (options) {
that.reload.call(that, options);
}
};
},
//构造器
Class = function (options) {
let index = ++dropdownTable.index;
this.index = index;
this.tableId = 'dropdown-select-table-' + index; //初始化当前下拉表格的ID
this.dropdownId = 'dropdown-' + index; //初始化当前下拉组件的ID
//其中selectedIds为初始化被选择的值,selectedDisplayValues为初始化被选中值的显示值
this.config = $.extend(true, {}, dropdownTable.config, options, {
selectedIds: [],
selectedDisplayValues: []
});
let config = this.config,
selectType = config.selectType,
selectField = { type: selectType, fixed: 'left' },
selectTableCols = config?.selectTable?.cols ?? [[]];
if (selectType === 'checkbox') {
selectTableCols.forEach((item) => {
if (JSON.stringify(item).indexOf(JSON.stringify(selectField)) < 0) {
item.splice(0, 0, selectField);
}
});
}
this.render();
};
//重载表格实例
Class.prototype.reloadSelectTable = function (selectTableConfig) {
let config = this.config;
layui.each(selectTableConfig, function (key, item) {
if (layui.type(item) === 'array') {
delete config.selectTable[key];
}
});
$.extend(true, config.selectTable, selectTableConfig);
this.clearSelected();
};
//清空选中
Class.prototype.clearSelected = function () {
let config = this.config,
bindInput = config.bindInput;
$(bindInput).val("");
$(bindInput)[0].dataset.displayValue = '';
$.extend(config, { selectedIds: [], selectedDisplayValues: [] });
this.setSelectedDisplayAndValue();
}
//渲染
Class.prototype.render = function () {
let that = this,
options = that.config,
_this = options.elem = $(options.elem);
if (!_this[0]) {
return;
}
that.initElemArea();
that.iniDropdown();
};
//初始化绑定元素区域
Class.prototype.initElemArea = function () {
let that = this,
config = this.config,
elem = config.elem,
searchName = this.config?.searchName ?? 'keywords';
$(elem).css({
'display': 'inline-block',
'width': '80%',
'height': '100%',
'min-height': '35px',
'border': '1px solid #eee',
'line-height': '35px',
'box-sizing': 'border-box',
'padding': '0 10px',
'cursor': 'pointer'
});
$(elem).hover(() => {
$(elem).css({ 'border-color': '#e2e2e2' });
}, () => {
$(elem).css({ 'border-color': '#eee' });
});
let selectButton = $("<button type='button' class='layui-btn layui-btn-primary'>选择</button>")
selectButton.css({
'display': 'inline-block',
'width': '20%',
'min-width': '45px',
'height': '100%',
'box-sizing': 'border-box',
'font-size': '12px',
'padding': '0',
'line-height': '36px'
});
if (config?.needPopup ?? true) {
$(elem).after(selectButton);
} else {
$(elem).css({ 'width': '100%' });
}
let selectContent = `
<div class="select-container" >
<div class="layui-row">
<div class="layui-col-md7 table-container">
<div class="layui-bg-green header">
<div class="layui-font-14">选择</div>
<form class="layui-form" style="float: right;" lay-filter="data-table-search-form">
<div class="layui-form-item" style="margin:0;">
<div class="layui-input-inline">
<input type="text" id="data_table_${searchName}" name="data_table_${searchName}" placeholder="请输入您要搜索的内容" class="layui-input">
</div>
<div class="layui-inline">
<button id="data-table-search-button" type="button" class="layui-btn layui-btn-primary" style="background-color:white"><i class="layui-icon layui-icon-search"></i></button>
</div>
</div>
</form>
</div>
<!--表格-->
<div class="table-item">
<table class="layui-hide" id="data-table" lay-filter="data-table"></table>
</div>
</div>
<div class="layui-col-md5 table-container">
<div class="layui-bg-green header">
<div class="layui-font-14">选择结果</div>
</div>
<div class="table-item">
<table class="layui-hide" id="selected-table" lay-filter="selected-table"></table>
<script type="text/html" id="selected-table-opt-bar">
<div class="layui-clear-space">
<a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del">移除</a>
</div>
</script>
</div>
</div>
</div>
</div>
<style>
.select-container{
width:100%;
padding:10px;
box-sizing:border-box;
}
.table-container {
box-sizing: border-box;
padding-right: 10px;
}
.table-container>.header {
width: 100%;
display: table;
padding: 5px 10px;
box-sizing: border-box;
min-height: 50px;
}
.table-container>.header>* {
display: table-cell;
vertical-align: middle;
}
.table-container>.table-item {
margin-top: 20px;
}
</style>
`
selectButton.click(() => {
layer.open({
title: '选择',
type: 1,
area: ['70%', '80%'],
maxmin: true,
btn: ['确认', '取消'],
content: selectContent,
success: () => {
renderSelectedTable();
renderDataTable();
bindTableEvent();
initSelectedTable();
bindDataTableSearchFormEvent();
},
yes: index => {
let selectedData = table.getData('selected-table') ?? [];
let selectedIds = selectedData.map(item => item.id);
config.selectedIds = selectedIds;
config.selectedDisplayValues = selectedData.map(item => item.displayValue);
that.setSelectedDisplayAndValue();
layer.close(index);
let onSelected = config.onSelected;
if ($.isFunction(onSelected)) {
onSelected(selectedIds);
}
}
});
});
function renderDataTable() {
let tableNeedConfig = {
id: 'data-table',
elem: '#data-table',
done: function (res) {
setDataTableSelected();
}
},
tableConfig = $.extend({}, config.selectTable, tableNeedConfig);
table.render(tableConfig);
}
function renderSelectedTable() {
table.render({
elem: '#selected-table',
cols: [
[
{ field: 'id', title: 'ID', width: 80, sort: true },
{ field: 'displayValue', title: '值' },
{ field: 'originValue', title: '原始值', hide: true },
{ fixed: 'right', title: '操作', width: 70, toolbar: '#selected-table-opt-bar' }
]
],
data: []
});
}
function bindDataTableSearchFormEvent() {
$('#data-table-search-button').click(() => {
let where = {},
searchValue = $('#data_table_' + searchName).val();
where[searchName] = searchValue;
table.reloadData('data-table', { page: { curr: 1 }, where: where });
});
}
function bindTableEvent() {
table.on('row(data-table)', (obj) => {
let data = obj.data;
selectedTableReload({
'id': data[config.selectTable.uniqueId],
'displayValue': data[config.selectTable.displayField],
'originValue': JSON.stringify(data)
});
});
table.on('tool(selected-table)', obj => {
if (obj.event === 'del') {
obj.del();
setDataTableSelected();
}
});
}
function selectedTableReload(selectedData) {
let originData = table.getData('selected-table') ?? [];
if (originData.map(item => item.id).includes(selectedData.id)) {
originData = originData.filter(item => item.id !== selectedData.id);
} else {
if (config.selectType === 'radio') {
originData = [];
}
originData.push(selectedData)
}
table.reload('selected-table', {
data: originData
});
setDataTableSelected();
}
function setDataTableSelected() {
let selectedIds = (table.getData('selected-table') ?? []).map(item => item.id + '');
let data = (table.getData('data-table') ?? []);
data.forEach((item, index) => {
if (config.selectType === 'checkbox') {
table.setRowChecked('data-table', {
type: 'checkbox',
index: index,
checked: selectedIds.includes(item.id + '')
});
} else if (selectedIds.includes(item.id + '')) {
table.setRowChecked('data-table', {
type: 'radio',
index: index,
checked: true
});
}
});
}
function initSelectedTable() {
let bindInput = config.bindInput,
bindInputValue = $(bindInput).val() ?? '',
bindInputDisplayValue = $(bindInput)[0].dataset.displayValue ?? '';
config.selectedIds = bindInputValue.split("$").filter(item => item !== '');
config.selectedDisplayValues = bindInputDisplayValue.split("$").filter(item => item !== '');
config.selectedIds.forEach((id, index) => {
selectedTableReload({
'id': id,
'displayValue': config.selectedDisplayValues[index]
});
});
}
}
//初始化下拉组件
Class.prototype.iniDropdown = function () {
let that = this,
config = that.config,
elem = config.elem;
dropdown.render({
id: config.id ?? that.index,
elem: elem,
trigger: 'click',
content: that.iniSelectTableHtml(),
style: config?.style ?? 'min-width:450px;box-sizing:border-box; padding:10px;width:' + $(elem).width() + 'px',
ready: () => {
that.iniSelectTable();
that.bindRowCheckToSelectTable();
that.bindSelectBoxEventToSelectTable();
that.bindSearchToSelectTable();
}
});
};
//初始化table的html文件
Class.prototype.iniSelectTableHtml = function () {
let index = this.index,
tableId = this.tableId,
searchName = this.config?.searchName ?? 'keywords',
selectTableSearchFormId = 'select-table-search-' + index,
selectTableSearchButtonId = 'select-table-search-button-' + index;
$.extend(this.config, {
selectTableSearchFormId: selectTableSearchFormId,
selectTableSearchButtonId: selectTableSearchButtonId
});
return [
'<div class="chosen-window">',
'<script type="text/html" id="' + selectTableSearchFormId + '">',
'<form class="layui-form layui-row">',
'<div class="layui-inline">',
'<div class="layui-input-inline">',
'<input type="text" name="' + searchName + '" placeholder="请输入要搜索的内容" class="layui-input">',
'</div>',
'</div>',
'<div class="layui-inline"><button class="layui-btn" lay-submit lay-filter="' + selectTableSearchButtonId + '">搜索</button></div>',
'</form>',
'</script>',
'<table class="layui-hide" id="' + tableId + '" lay-filter="' + tableId + '"></table>',
'</div>',
].join('');
}
//初始化表格
Class.prototype.iniSelectTable = function () {
let that = this,
config = that.config,
tableId = that.tableId,
selectTable = config.selectTable,
tableNeedConfig = {
id: tableId,
elem: '#' + tableId,
toolbar: '#' + config.selectTableSearchFormId,
defaultToolbar: ['filter'],
done: function (res) {
that.iniDefaultSelected(res.data)
}
},
tableConfig = $.extend({}, selectTable, tableNeedConfig);
table.render(tableConfig);
};
//处理默认选中值的问题
Class.prototype.iniDefaultSelected = function (tableData = []) {
let that = this,
tableId = that.tableId,
config = that.config,
selectType = config.selectType,
bindInput = config.bindInput,
bindInputValue = $(bindInput).val() ?? '',
bindInputDisplayValue = $(bindInput)[0].dataset.displayValue ?? '';
config.selectedIds = bindInputValue.split("$").filter(item => item !== '');
config.selectedDisplayValues = bindInputDisplayValue.split("$").filter(item => item !== '');
if (tableData.length === 0) {
that.setSelectedDisplayAndValue();
return;
}
table.setRowChecked(tableId, { index: 'all', checked: false });
let selectedIds = config.selectedIds;
if (selectedIds.length > 0) {
let tableIdData = tableData.map(item => item[config.selectTable.uniqueId].toString());
if (selectType === 'radio' && selectedIds.length === 1) {
let selectedItemIndex = tableIdData.indexOf(selectedIds[0].toString());
if (selectedItemIndex >= 0) {
table.setRowChecked(tableId, { type: selectType, index: selectedItemIndex, checked: true })
}
} else {
tableIdData.forEach(function (item, itemIndex) {
table.setRowChecked(tableId, {
type: selectType,
index: itemIndex,
checked: selectedIds.includes(item)
});
});
}
}
that.setSelectedDisplayAndValue();
}
//处理选择框事件
Class.prototype.bindSelectBoxEventToSelectTable = function () {
let that = this,
options = that.config,
selectType = options.selectType,
uniqueId = options.selectTable.uniqueId,
displayField = options.selectTable.displayField,
tableId = that.tableId;
if (selectType === 'radio') { //单选
table.on('radio(' + tableId + ')', function (obj) {
dealRadioSelectedData(obj.checked, obj.data);
});
} else { //复选框
table.on('checkbox(' + tableId + ')', function (obj) {
let eventType = obj.type,
checked = obj.checked;
if (eventType === 'all') {
table.getData(tableId).forEach(function (rowData) {
dealCheckboxSelectedData(checked, rowData);
});
}
if (eventType === 'one') {
dealCheckboxSelectedData(checked, obj.data);
}
});
}
function dealRadioSelectedData(checked, rowData) {
let rowId = rowData[uniqueId].toString();
that.config.selectedIds = [];
that.config.selectedDisplayValues = [];
if (checked) {
that.config.selectedIds.push(rowId);
that.config.selectedDisplayValues.push(rowData[displayField]);
}
that.setSelectedDisplayAndValue();
}
function dealCheckboxSelectedData(checked, rowData) {
let rowId = rowData[uniqueId].toString();
//处理选中
if (checked && !that.config.selectedIds.includes(rowId)) {
that.config.selectedIds.push(rowId);
that.config.selectedDisplayValues.push(rowData[displayField]);
}
//处理未选中
if (!checked) {
let cancelIndex = that.config.selectedIds.indexOf(rowId);
if (rowId > -1) {
that.config.selectedIds.splice(cancelIndex, 1);
that.config.selectedDisplayValues.splice(cancelIndex, 1);
}
}
that.setSelectedDisplayAndValue();
}
}
//绑定行点击事件到选择表中
Class.prototype.bindRowCheckToSelectTable = function () {
let that = this,
tableId = that.tableId,
options = that.config,
selectType = options.selectType,
uniqueId = options.selectTable.uniqueId,
displayField = options.selectTable.displayField;
table.on('row(' + tableId + ')', function (obj) {
let rowId = obj.data[uniqueId].toString(),
checked;
if (!that.config.selectedIds.includes(rowId)) {
if (selectType === 'radio') {
that.config.selectedIds = [];
that.config.selectedDisplayValues = [];
}
that.config.selectedIds.push(rowId);
that.config.selectedDisplayValues.push(obj.data[displayField]);
checked = true;
} else {
let cancelSelectedIdIndex = that.config.selectedIds.indexOf(rowId);
that.config.selectedIds.splice(cancelSelectedIdIndex, 1);
that.config.selectedDisplayValues.splice(cancelSelectedIdIndex, 1);
checked = false;
}
obj.setRowChecked({
type: selectType,
checked: checked
});
//加入选中事件
let onSelected = options.onSelected;
if (checked && $.isFunction(onSelected)) {
onSelected(new Array(obj.data[options.selectTable.uniqueId]));
}
that.setSelectedDisplayAndValue();
});
};
//绑定表格的搜索事件
Class.prototype.bindSearchToSelectTable = function () {
let tableId = this.tableId,
selectTableSearchButtonId = this.config.selectTableSearchButtonId;
form.on('submit(' + selectTableSearchButtonId + ')', function (data) {
table.reloadData(tableId, { page: { curr: 1 }, where: data.field });
return false;
});
}
//设置显示值和实际存储值
Class.prototype.setSelectedDisplayAndValue = function () {
let that = this,
tableId = that.tableId,
options = that.config,
elem = options.elem,
bindInput = options.bindInput,
selectedIds = options?.selectedIds ?? [],
selectDisplayValues = options.selectedDisplayValues,
emptyMsg = options.emptyMsg;
elem.find("span").remove();
if (selectedIds.length > 0 && selectedIds.length === selectDisplayValues.length) {
selectedIds.forEach((item, itemIndex) => {
let selectedDisplayValue = selectDisplayValues[itemIndex],
closeObject = $('<span style="margin-left:5px;color:#fff" data-selected-id="' + item + '">×<span>'),
displayHtml = [
'<span class="layui-badge layui-bg-green">' + selectedDisplayValue,
'</span>',
].join(''),
displayObject = $(displayHtml).append(closeObject);
displayObject.attr({
"style": "margin-left:5px;font-size:14px;padding:4px 6px"
});
//绑定鼠标事件
closeObject.on('mouseenter mouseleave', function (event) {
let optType = event.type,
fontColor = optType === 'mouseleave' ? 'white' : 'red';
$(this).attr({ "style": "color:" + fontColor + ";margin-left:5px;cursor:pointer" });
});
closeObject.on('click', function () {
let _this = this,
tableIdData = table.getData(tableId).map(item => item[options.selectTable.uniqueId]),
selectedId = _this.dataset.selectedId,
cancelSelectedIdIndex = that.config.selectedIds.indexOf(selectedId);
that.config.selectedIds.splice(cancelSelectedIdIndex, 1);
that.config.selectedDisplayValues.splice(cancelSelectedIdIndex, 1);
if (tableIdData.includes(selectedId)) {
if (options.selectType === 'radio') {
table.setRowChecked(tableId, {
type: 'radio',
index: 'all',
checked: false
});
} else {
table.setRowChecked(tableId, {
type: options.selectType,
index: tableIdData.indexOf(selectedId),
checked: false
});
}
}
that.setSelectedDisplayAndValue();
});
elem.append(displayObject);
});
} else {
elem.append('<span class="layui-font-gray layui-font-14" style="margin-left:5px;">' + emptyMsg + '</span>');
}
$(bindInput).val(selectedIds.join("$"));
$(bindInput)[0].dataset.displayValue = selectDisplayValues.join("$");
}
//记录所有实例
thisModule.that = {}; //记录所有实例对象
//获取当前实例对象
thisModule.getThis = function (id) {
let that = thisModule.that[id];
if (!that) {
hint.error(id ? (moduleName + ' instance with ID \'' + id + '\' not found') : 'ID argument required');
}
return that;
}
//重载实例
dropdownTable.reloadSelectTable = function (id, selectTableConfig) {
let that = thisModule.getThis(id);
that.reloadSelectTable(selectTableConfig);
return thisModule.call(that);
}
//请空已选择
dropdownTable.clearSelected = function (id) {
let that = thisModule.getThis(id);
that.clearSelected();
}
//核心入口
dropdownTable.render = function (options) {
let inst = new Class(options);
if (dropdownTable.index === 1) {
console.log("欢迎使用Hg科技的dropdownTable组件,version:" + dropdownTable.version + ",期待您的建议!");
}
inst.iniDefaultSelected();
return thisModule.call(inst);
}
exports(moduleName, dropdownTable);
});
File diff suppressed because one or more lines are too long
@@ -0,0 +1,492 @@
layui.define(function(exports) {
exports('echartsTheme',
{
"color": [
"#3fb1e3",
"#6be6c1",
"#626c91",
"#a0a7e6",
"#c4ebad",
"#96dee8"
],
"backgroundColor": "rgba(252,252,252,0)",
"textStyle": {},
"title": {
"textStyle": {
"color": "#666666"
},
"subtextStyle": {
"color": "#999999"
}
},
"line": {
"itemStyle": {
"normal": {
"borderWidth": "3"
}
},
"lineStyle": {
"normal": {
"width": "4"
}
},
"symbolSize": "10",
"symbol": "emptyCircle",
"smooth": true
},
"radar": {
"itemStyle": {
"normal": {
"borderWidth": "3"
}
},
"lineStyle": {
"normal": {
"width": "4"
}
},
"symbolSize": "10",
"symbol": "emptyCircle",
"smooth": true
},
"bar": {
"itemStyle": {
"normal": {
"barBorderWidth": 0,
"barBorderColor": "#ccc"
},
"emphasis": {
"barBorderWidth": 0,
"barBorderColor": "#ccc"
}
}
},
"pie": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"scatter": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"boxplot": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"parallel": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"sankey": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"funnel": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"gauge": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
},
"emphasis": {
"borderWidth": 0,
"borderColor": "#ccc"
}
}
},
"candlestick": {
"itemStyle": {
"normal": {
"color": "#e6a0d2",
"color0": "transparent",
"borderColor": "#e6a0d2",
"borderColor0": "#3fb1e3",
"borderWidth": "2"
}
}
},
"graph": {
"itemStyle": {
"normal": {
"borderWidth": 0,
"borderColor": "#ccc"
}
},
"lineStyle": {
"normal": {
"width": "1",
"color": "#cccccc"
}
},
"symbolSize": "10",
"symbol": "emptyCircle",
"smooth": true,
"color": [
"#3fb1e3",
"#6be6c1",
"#626c91",
"#a0a7e6",
"#c4ebad",
"#96dee8"
],
"label": {
"normal": {
"textStyle": {
"color": "#ffffff"
}
}
}
},
"map": {
"itemStyle": {
"normal": {
"areaColor": "#eeeeee",
"borderColor": "#aaaaaa",
"borderWidth": 0.5
},
"emphasis": {
"areaColor": "rgba(63,177,227,0.25)",
"borderColor": "#3fb1e3",
"borderWidth": 1
}
},
"label": {
"normal": {
"textStyle": {
"color": "#ffffff"
}
},
"emphasis": {
"textStyle": {
"color": "rgb(63,177,227)"
}
}
}
},
"geo": {
"itemStyle": {
"normal": {
"areaColor": "#eeeeee",
"borderColor": "#aaaaaa",
"borderWidth": 0.5
},
"emphasis": {
"areaColor": "rgba(63,177,227,0.25)",
"borderColor": "#3fb1e3",
"borderWidth": 1
}
},
"label": {
"normal": {
"textStyle": {
"color": "#ffffff"
}
},
"emphasis": {
"textStyle": {
"color": "rgb(63,177,227)"
}
}
}
},
"categoryAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"valueAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"logAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"timeAxis": {
"axisLine": {
"show": true,
"lineStyle": {
"color": "#cccccc"
}
},
"axisTick": {
"show": false,
"lineStyle": {
"color": "#333"
}
},
"axisLabel": {
"show": true,
"textStyle": {
"color": "#999999"
}
},
"splitLine": {
"show": true,
"lineStyle": {
"color": [
"#eeeeee"
]
}
},
"splitArea": {
"show": false,
"areaStyle": {
"color": [
"rgba(250,250,250,0.05)",
"rgba(200,200,200,0.02)"
]
}
}
},
"toolbox": {
"iconStyle": {
"normal": {
"borderColor": "#999999"
},
"emphasis": {
"borderColor": "#666666"
}
}
},
"legend": {
"textStyle": {
"color": "#999999"
}
},
"tooltip": {
"axisPointer": {
"lineStyle": {
"color": "#cccccc",
"width": 1
},
"crossStyle": {
"color": "#cccccc",
"width": 1
}
}
},
"timeline": {
"lineStyle": {
"color": "#626c91",
"width": 1
},
"itemStyle": {
"normal": {
"color": "#626c91",
"borderWidth": 1
},
"emphasis": {
"color": "#626c91"
}
},
"controlStyle": {
"normal": {
"color": "#626c91",
"borderColor": "#626c91",
"borderWidth": 0.5
},
"emphasis": {
"color": "#626c91",
"borderColor": "#626c91",
"borderWidth": 0.5
}
},
"checkpointStyle": {
"color": "#3fb1e3",
"borderColor": "rgba(63,177,227,0.15)"
},
"label": {
"normal": {
"textStyle": {
"color": "#626c91"
}
},
"emphasis": {
"textStyle": {
"color": "#626c91"
}
}
}
},
"visualMap": {
"color": [
"#2a99c9",
"#afe8ff"
]
},
"dataZoom": {
"backgroundColor": "rgba(255,255,255,0)",
"dataBackgroundColor": "rgba(222,222,222,1)",
"fillerColor": "rgba(114,230,212,0.25)",
"handleColor": "#cccccc",
"handleSize": "100%",
"textStyle": {
"color": "#999999"
}
},
"markPoint": {
"label": {
"normal": {
"textStyle": {
"color": "#ffffff"
}
},
"emphasis": {
"textStyle": {
"color": "#ffffff"
}
}
}
}
});
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,87 @@
@keyframes fariy-fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.fairy-tag-container {
width: auto;
min-height: 100px;
padding: 5px;
border: 1px solid #e6e6e6;
background-color: #ffffff;
}
.fairy-tag-container:hover {
border-color: #d2d2d2;
}
.fairy-tag-container span.fairy-tag {
float: left;
font-size: 13px;
padding: 5px 8px;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 2px;
line-height: 16px;
}
.fairy-tag-container span.fairy-tag a {
font-size: 11px;
font-weight: bolder;
color: #ffffff;
text-decoration: none;
margin-left: 6px;
}
.fairy-tag-container span.fairy-tag a:hover {
cursor: pointer;
}
.fairy-tag-container span.fairy-bg-red {
background-color: #FF5722;
}
.fairy-tag-container span.fairy-bg-orange {
background-color: #FFB800;
}
.fairy-tag-container span.fairy-bg-green {
background-color: #009688;
}
.fairy-tag-container span.fairy-bg-cyan {
background-color: #2F4056;
}
.fairy-tag-container span.fairy-bg-blue {
background-color: #1E9FFF;
}
.fairy-tag-container span.fairy-bg-black {
background-color: #393D49;
}
.fairy-tag-container span.fairy-bg-red,
.fairy-tag-container span.fairy-bg-orange,
.fairy-tag-container span.fairy-bg-green,
.fairy-tag-container span.fairy-bg-cyan,
.fairy-tag-container span.fairy-bg-blue,
.fairy-tag-container span.fairy-bg-black {
color: #ffffff;
}
.fairy-tag-container .fairy-anim-fadein {
animation: fariy-fadein 0.3s both;
}
.fairy-tag-container .fairy-tag-input[type='text'] {
width: 80px;
font-size: 13px;
padding: 6px;
background: transparent;
border: 0 none;
outline: 0;
}
.fairy-tag-container .fairy-tag-input[type='text']:focus::-webkit-input-placeholder {
color: transparent;
}
.fairy-tag-container .fairy-tag-input[type='text']:focus:-moz-placeholder {
color: transparent;
}
.fairy-tag-container .fairy-tag-input[type='text']:focus:-moz-placeholder {
color: transparent;
}
.fairy-tag-container .fairy-tag-input[type='text']:focus:-ms-input-placeholder {
color: transparent;
}
/*# sourceMappingURL=inputTag.css.map */
+150
View File
@@ -0,0 +1,150 @@
/*
* Name: inputTag
* Author: cshaptx4869
* Project: https://github.com/cshaptx4869/inputTag
*/
(function (define) {
define(['jquery'], function ($) {
"use strict";
class InputTag {
options = {
elem: '.fairy-tag-input',
theme: ['fairy-bg-red', 'fairy-bg-orange', 'fairy-bg-green', 'fairy-bg-cyan', 'fairy-bg-blue', 'fairy-bg-black'],
data: [],
removeKeyNum: 8,
createKeyNum: 13,
permanentData: [],
};
get elem() {
return $(this.options.elem);
}
get copyData() {
return [...this.options.data];
}
constructor(options) {
this.render(options);
}
render(options) {
this.init(options);
this.listen();
}
init(options) {
var spans = '', that = this;
this.options = $.extend(this.options, options);
!this.elem.attr('placeholder') && this.elem.attr('placeholder', '添加标签');
$.each(this.options.data, function (index, item) {
spans += that.spanHtml(item);
});
this.elem.before(spans);
}
listen() {
var that = this;
this.elem.parent().on('click', 'a', function () {
that.removeItem($(this).parent('span'));
});
this.elem.parent().on('click', function () {
that.elem.focus();
});
this.elem.keydown(function (event) {
var keyNum = (event.keyCode ? event.keyCode : event.which);
if (keyNum === that.options.removeKeyNum) {
if (!that.elem.val().trim()) {
var closeItems = that.elem.parent().find('a');
if (closeItems.length) {
that.removeItem($(closeItems[closeItems.length - 1]).parent('span'));
event.preventDefault();
}
}
} else if (keyNum === that.options.createKeyNum) {
that.createItem();
event.preventDefault();
}
});
}
createItem() {
var value = this.elem.val().trim();
if (this.options.beforeCreate && typeof this.options.beforeCreate === 'function') {
var modifiedValue = this.options.beforeCreate(this.copyData, value);
if (typeof modifiedValue == 'string' && modifiedValue) {
value = modifiedValue;
} else {
value = '';
}
}
if (value) {
if (!this.options.data.includes(value)) {
this.options.data.push(value);
this.elem.before(this.spanHtml(value));
this.onChange(value, 'create');
}
}
this.elem.val('');
}
removeItem(target) {
var that = this;
var closeSpan = target.remove(),
closeSpanText = $(closeSpan).children('span').text();
var value = that.options.data.splice($.inArray(closeSpanText, that.options.data), 1);
value.length === 1 && that.onChange(value[0], 'remove');
}
randomColor() {
return this.options.theme[Math.floor(Math.random() * this.options.theme.length)];
}
spanHtml(value) {
return '<span class="fairy-tag fairy-anim-fadein ' + this.randomColor() + '">' +
'<span>' + value + '</span>' +
(this.options.permanentData.includes(value) ? '' : '<a href="#" title="删除标签">&times;</a>') +
'</span>';
}
onChange(value, type) {
this.options.onChange && typeof this.options.onChange === 'function' && this.options.onChange(this.copyData, value, type);
}
getData() {
return this.copyData;
}
clearData() {
this.options.data = [];
this.elem.prevAll('span.fairy-tag').remove();
}
}
return {
render(options) {
return new InputTag(options);
}
}
});
}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
layui.link(layui.cache.base + "inputTag/inputTag.css?v="+(new Date).getTime());
var MOD_NAME = 'inputTag';
if (typeof module !== 'undefined' && module.exports) { //Node
module.exports = factory(require('jquery'));
} else if (window.layui && layui.define) {
layui.define('jquery', function (exports) { //layui加载
exports(MOD_NAME, factory(layui.jquery));
});
} else {
window[MOD_NAME] = factory(window.jQuery);
}
}));
File diff suppressed because one or more lines are too long
+80
View File
@@ -0,0 +1,80 @@
.jmSheet {
width: 100%;
background-color: #f8f8f8 !important;
}
.jmSheet li {
height: 55px;
border-bottom: 1px solid #eee;
display: flex;
/* justify-content: center; */
align-items: center;
box-sizing: border-box;
background-color: #fff;
gap: 10px;
padding: 0 10px;
}
.jmSheet li>.text {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.jmSheet li:hover {
cursor: pointer;
}
.jmSheet li:hover span {
color: #16b777;
}
.jmSheet li .img {
position: relative;
width: 24px;
height: 24px;
overflow: hidden;
border-radius: 5px;
}
.jmSheet li .img img {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
}
.jmSheet li span {
font-size: 14px;
color: rgba(0, 0, 0, .8);
font-weight: 600;
}
.jmSheet li .desc {
font-weight: normal;
color: #c2c2c2;
font-size: 12px;
}
.jmSheet .close {
height: 55px;
margin-top: 8px;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
}
.jmSheet>h3 {
height: 40px;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
font-weight: normal;
font-size: 14px;
border-bottom: 1px solid #eee;
color: #00000080;
box-sizing: border-box;
}
+88
View File
@@ -0,0 +1,88 @@
layui.define(["jquery", "layer"], function (exports) {
"use strict";
var $ = layui.$;
var layer = layui.layer;
var jmSheet = {
open: function (obj) {
if (typeof obj.shadeClose === 'undefined') {
obj.shadeClose = true;
}
var align = "center"
if (typeof obj.align !== "undefined" && obj.align != "") {
switch (obj.align) {
case "left":
align = "flex-start";
break;
case "right":
align = "flex-end";
break;
default:
align = "center";
}
}
var html = "";
$.each(obj.content, function (k, v) {
var img = "";
if (typeof v.img !== 'undefined' && v.img != "") {
img = `<div class="img"><img src="${v.img}"></div>`;
}
html += `<li style="justify-content:${align};">
${img}
<div class="text">
<span>${v.text}</span>
<span class="desc">${v.desc || ''}</span>
</div>
</li>`
})
var btnClose = "";
var btnCloseSize = 0;
if (typeof obj.shadeClose !== 'undefined' && !obj.shadeClose) {
btnClose = `<a href="javascript:;" class="close">取消</a>`;
btnCloseSize = (55 + 8);
}
var title = "";
var titleSize = 0;
if (typeof obj.title !== 'undefined' && obj.title != "") {
title = `<h3>${obj.title}</h3>`;
titleSize = 40;
}
//弹出
var open = layer.open({
type: 1,
title: false,
closeBtn: 0,
offset: 'b',
anim: 'slideUp', // 从下往上
area: ['100%', titleSize + btnCloseSize + 55 * obj.content.length + 'px'],
shade: 0.1,
shadeClose: obj.shadeClose, // 是否点击遮罩关闭
id: obj.id || '',
content:
`<div class="jmSheet ${obj.addClass || ''}">
${title}
<ul>${html}</ul>
${btnClose}
</div>`
});
//关闭
$(`#layui-layer${open} .jmSheet .close`).on("click", function () {
layer.close(open);
})
//点击列表
$(`#layui-layer${open} .jmSheet`).on("click", "li", function () {
obj.callback($(this).index(), this, obj.content[$(this).index()]);
})
}
}
exports('jmSheet', jmSheet); // 输出模块
}).link(layui.cache.base+"jmSheet/jmSheet.css?v="+(new Date).getTime());
File diff suppressed because it is too large Load Diff
+177
View File
@@ -0,0 +1,177 @@
/**
* layselect 下拉框插件,只支持单选
* @author:Darker.Wang
* @version:1.1(优化增加数据内部控制元素默认选择)
* @version:1.2(优化支持指定请求头认证,修复元素默认选择优先级错乱)
* render参数{
* 属性:
* elem:元素ID,带#号必传
* url:请求路径的URL,必传
* data:请求url所携带的参数,可选
* type:请求方式,默认为get,可选
* option:元素数据,数组,用于不通过请求url获取数据,本地自动赋值,可选
* select:指定选中的索引项,可选,分组时索引为:groupNum-itemNum,如:1-2 表示第一组里的第二个元素
* 方法:
* format:格式化方法映射,将返回的Data元素映射乘标准格式
* success:成功回调,返回加载后的对象数组
* fail:失败回调,加载失败的处理
* onselect:点击选择时事件响应(如事件无响应,记得加lay-filter属性=id
* headers:可选,用于传递请求头认证,默认为:{Accept: "application/json; charset=utf-8"}
* contentType:可选,用于指定请求接口的数据类型,默认为:application/json
* }
* 请求返回需对象格式:rtvObj=option={status,code,codeName,select,...},不满足的通过format映射处理 status=0 时表示禁用
* 其他说明:
* 1、分组展示按照:groupNamegroupChildren 数组 [rtvObj]
* 2、暂不支持多选(后期版本规划:支持多选,在select标签上设置属性:multiple="true"
* 3、获取原始数据,可通过调用success得到,返回原始数据的集合
* 4、点击事件回调,可通过实现onselect 触发,参数为当前点击的值
* @param exports
* @returns 返回一个对象
* 码云地址:https://gitee.com/godbirds/layselect
*/
layui.define(['element','form','jquery'],function(exports){
var element = layui.element;
var form = layui.form;
var $ = layui.jquery;
var obj={
//{elem,url,data,type,format}
render:function(param){
var that = this;
var eid = param.elem;
that.selectValues=new Array();
if(param.type == null || param.type==undefined){
param.type = 'get';//默认get请求
}
if(param.data){
param.data = JSON.stringify(data);
}else{
param.data = {}
}
if(param.url == null || param.url == '' || param.url==undefined){
if(param.option == null || param.option == undefined){
param.option = new Array();//重新定义为了正常显示下拉框
}
that._init(eid,param,param.option);
}else{
$.ajax({
url: param.url,
type: param.type,
data: param.data,
async: 'false',
dataType: 'json',
headers: param.headers||{Accept: "application/json; charset=utf-8"},
contentType : param.contentType||'application/json',//指定json头
success: function(data){
that._init(eid,param,data)
},
error: function (e) {
//加载失败执行回调函数
console.log("Ajax request "+param.url+" error! ");
if(param.fail){
param.fail(e);//加载失败回调函数,请求URL时才会触发
}
}
});
}
/***
* 初始化:data=[{code,status,codeName,select,groupName,groupChildren}]
*/
that._init=function(eid,param,data){
$(eid).empty();//请求成功时清空
$(eid).prepend("<option value=''>请选择</option>");//添加第一个option值
var option = new Array();
if(param.format){//格式化
for (var i = 0; i < data.length; i++) {
var tdata = param.format(data[i]);
option.push({
code:tdata.code,
status:tdata.status||'1',
select:tdata.select||'false',
codeName:tdata.codeName,
groupName:tdata.groupName,
groupChildren:tdata.groupChildren
});
}
}else{//不格式化
for (var i = 0; i < data.length; i++) {
option.push({
code:data[i].code,
status:data[i].status||'1',
select:data[i].select||'false',
codeName:data[i].codeName,
groupName:data[i].groupName,
groupChildren:data[i].groupChildren
});
}
}
var isgroup = false;
for (var i = 0; i < option.length; i++) {
//分组
if(option[i].groupChildren != null && option[i].groupChildren != undefined &&
option[i].groupChildren != '' && option[i].groupChildren.length > 0){
isgroup = true;//分组标识
$(eid).append("<optgroup label='"+option[i].groupName+"'>");
option[i].groupChildren.forEach(function(item,index){
var status = "";var topborder ="",buttomborder="",checkoff="";
if(item.status && item.status == '0'){status = "disabled='disabled'";}//是否有效
$(eid).append("<option value='"+item.code+"' "+status+">"+item.codeName+"</option>");
//优先级:内部数据option中的选中标识优先级高于外部指定的优先级
if(item.select != undefined && (item.select == 'true' || item.select == true) && item.status != '0'){
that.selectValues.push(item);
}
});
$(eid).append("</optgroup>");
}else{//不分组
isgroup = false;//分组标识
var status = "";var topborder ="",buttomborder="",checkoff="";
if(option[i].status && option[i].status == '0'){status = "disabled='disabled'";}//是否有效
$(eid).append("<option value='"+option[i].code+"' "+status+">"+option[i].codeName+"</option>");
//内部数据option中的选中标识优先级高于外部指定的优先级
if(option[i].select != undefined && (option[i].select == 'true' || option[i].select == true) && option[i].status != '0'){
that.selectValues.push(option[i]);
}
}
}
//内部无选中则判断外部是否指定,遵循内部优先级高于外部
if(that.selectValues.length == 0 && param.select != undefined){
var item;
var si = param.select+"";
if(isgroup && si.indexOf("-") > 0){//分组时索引为指定为 group-item
var s = si.split("-");//拆分组
item = option[s[0]].groupChildren[s[1]];
}else{
item = option[si];
}
if(item && item.status != '0'){
that.selectValues.push(item);
}
}//选择指定选中项
//var k = eid.replace('#','');
var multiple = $(eid).attr('multiple');//多选控制,暂时无用
//console.log(eid,that.selectValues);
if(that.selectValues.length>0){//默认选中数据在循环外部,提高渲染效率
that.selectValues.forEach(function(item,index){
$(eid).find("option[value = '"+item.code+"']").attr("selected","selected");
});
}else{
//$(eid).find("option[value = '']").attr("selected","selected");//默认选中请选择
}
//加载成功执行回调函数
if(param.success){
//console.log(k+" call success",option);
param.success(option);
option = null;
}
//监听事件:这里做自己想做的事情
form.render('select');//select是固定写法 不是选择器
form.on('select('+eid.replace('#','')+')', function(data){
if(param.onselect){//点击选择事件
param.onselect(data.value);
}
});
}
}
};
exports('layselect',obj);
});
+368
View File
@@ -0,0 +1,368 @@
/**
* date:2020/02/27
* author:Mr.Chung
* version:2.0
* description:layuimini 主体框架扩展
*/
layui.define(["jquery", "miniMenu", "element", "miniTab", "miniTheme"], function (exports) {
var $ = layui.$,
layer = layui.layer,
miniMenu = layui.miniMenu,
miniTheme = layui.miniTheme,
element = layui.element ,
miniTab = layui.miniTab;
if (!/http(s*):\/\//.test(location.href)) {
var tips = "请先将项目部署至web容器(Apache/Tomcat/Nginx/IIS/等),否则部分数据将无法显示";
return layer.alert(tips);
}
var miniAdmin = {
/**
* 后台框架初始化
* @param options.iniUrl 后台初始化接口地址
* @param options.clearUrl 后台清理缓存接口
* @param options.urlHashLocation URL地址hash定位
* @param options.bgColorDefault 默认皮肤
* @param options.multiModule 是否开启多模块
* @param options.menuChildOpen 是否展开子菜单
* @param options.loadingTime 初始化加载时间
* @param options.pageAnim iframe窗口动画
* @param options.maxTabNum 最大的tab打开数量
*/
render: function (options) {
options.iniUrl = options.iniUrl || null;
options.clearUrl = options.clearUrl || null;
options.urlHashLocation = options.urlHashLocation || false;
options.bgColorDefault = options.bgColorDefault || 0;
options.multiModule = options.multiModule || false;
options.menuChildOpen = options.menuChildOpen || false;
options.loadingTime = options.loadingTime || 0;
options.pageAnim = options.pageAnim || false;
options.maxTabNum = options.maxTabNum || 200;
options.isHideOpenMenu = !options.isHideOpenMenu ? options.isHideOpenMenu : true;
$.getJSON(options.iniUrl, function (data) {
if (data == null) {
miniAdmin.error('暂无菜单信息')
} else {
miniAdmin.renderLogo(data.logoInfo);
miniAdmin.renderClear(options.clearUrl);
miniAdmin.renderHome(data.homeInfo);
miniAdmin.renderAnim(options.pageAnim);
miniAdmin.listen();
miniMenu.render({
menuList: data.menuInfo,
multiModule: options.multiModule,
menuChildOpen: options.menuChildOpen
});
miniTab.render({
filter: 'layuiminiTab',
urlHashLocation: options.urlHashLocation,
multiModule: options.multiModule,
menuChildOpen: options.menuChildOpen,
maxTabNum: options.maxTabNum,
menuList: data.menuInfo,
homeInfo: data.homeInfo,
listenSwichCallback: function () {
miniAdmin.renderDevice();
}
});
miniTheme.render({
bgColorDefault: options.bgColorDefault,
listen: true,
});
miniAdmin.deleteLoader(options.loadingTime);
miniAdmin.isHideOpenMenu(options.isHideOpenMenu);
}
}).fail(function () {
miniAdmin.error('菜单接口有误');
});
},
/**
* 点击左侧下拉菜单,关闭其他下拉菜单
* @param data
*/
isHideOpenMenu: function (data) {
$(".layui-side").on("click", ".layui-nav-item", function () {
if (data) {
$(this).siblings('li').attr('class', 'layui-nav-item');
}
});
},
/**
* 初始化logo
* @param data
*/
renderLogo: function (data) {
var html = '<a href="' + data.href + '"><img src="' + data.image + '" style="width:95%;" alt="logo"><h1>' + data.title + '</h1></a>';
$('.layuimini-logo').html(html);
},
/**
* 初始化首页
* @param data
*/
renderHome: function (data) {
sessionStorage.setItem('layuiminiHomeHref', data.href);
$('#layuiminiHomeTabId').html('<span class="layuimini-tab-active"></span><span class="disable-close">' + data.title + '</span><i class="layui-icon layui-unselect layui-tab-close">ဆ</i>');
$('#layuiminiHomeTabId').attr('lay-id', data.href);
$('#layuiminiHomeTabIframe').html('<iframe width="100%" height="100%" frameborder="no" border="0" marginwidth="0" marginheight="0" src="' + data.href + '"></iframe>');
},
/**
* 初始化缓存地址
* @param clearUrl
*/
renderClear: function (clearUrl) {
$('.layuimini-clear').attr('data-href',clearUrl);
},
/**
* 初始化iframe窗口动画
* @param anim
*/
renderAnim: function (anim) {
if (anim) {
$('#layuimini-bg-color').after('<style id="layuimini-page-anim">' +
'.layui-tab-item.layui-show {animation:moveTop 1s;-webkit-animation:moveTop 1s;animation-fill-mode:both;-webkit-animation-fill-mode:both;position:relative;height:100%;-webkit-overflow-scrolling:touch;}\n' +
'@keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}\n' +
'@-o-keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}\n' +
'@-moz-keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}\n' +
'@-webkit-keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}' +
'</style>');
}
},
fullScreen: function () {
var el = document.documentElement;
var rfs = el.requestFullScreen || el.webkitRequestFullScreen;
if (typeof rfs != "undefined" && rfs) {
rfs.call(el);
} else if (typeof window.ActiveXObject != "undefined") {
var wscript = new ActiveXObject("WScript.Shell");
if (wscript != null) {
wscript.SendKeys("{F11}");
}
} else if (el.msRequestFullscreen) {
el.msRequestFullscreen();
} else if (el.oRequestFullscreen) {
el.oRequestFullscreen();
} else if (el.webkitRequestFullscreen) {
el.webkitRequestFullscreen();
} else if (el.mozRequestFullScreen) {
el.mozRequestFullScreen();
} else {
miniAdmin.error('浏览器不支持全屏调用!');
}
},
/**
* 退出全屏
*/
exitFullScreen: function () {
var el = document;
var cfs = el.cancelFullScreen || el.webkitCancelFullScreen || el.exitFullScreen;
if (typeof cfs != "undefined" && cfs) {
cfs.call(el);
} else if (typeof window.ActiveXObject != "undefined") {
var wscript = new ActiveXObject("WScript.Shell");
if (wscript != null) {
wscript.SendKeys("{F11}");
}
} else if (el.msExitFullscreen) {
el.msExitFullscreen();
} else if (el.oRequestFullscreen) {
el.oCancelFullScreen();
}else if (el.mozCancelFullScreen) {
el.mozCancelFullScreen();
} else if (el.webkitCancelFullScreen) {
el.webkitCancelFullScreen();
} else {
miniAdmin.error('浏览器不支持全屏调用!');
}
},
/**
* 初始化设备端
*/
renderDevice: function () {
if (miniAdmin.checkMobile()) {
$('.layuimini-tool i').attr('data-side-fold', 1);
$('.layuimini-tool i').attr('class', 'layui-icon layui-icon-list');
$('.layui-layout-body').removeClass('layuimini-mini');
$('.layui-layout-body').addClass('layuimini-all');
}
},
/**
* 初始化加载时间
* @param loadingTime
*/
deleteLoader: function (loadingTime) {
if (loadingTime) {
setTimeout(function () {
$('.layuimini-loader').fadeOut();
}, loadingTime * 1000)
} else {
$('.layuimini-loader').fadeOut();
}
},
/**
* 成功
* @param title
* @returns {*}
*/
success: function (title) {
return layer.msg(title, {icon: 1, shade: this.shade, scrollbar: false, time: 2000, shadeClose: true});
},
/**
* 失败
* @param title
* @returns {*}
*/
error: function (title) {
return layer.msg(title, {icon: 2, shade: this.shade, scrollbar: false, time: 3000, shadeClose: true});
},
/**
* 判断是否为手机
* @returns {boolean}
*/
checkMobile: function () {
var ua = navigator.userAgent.toLocaleLowerCase();
var pf = navigator.platform.toLocaleLowerCase();
var isAndroid = (/android/i).test(ua) || ((/iPhone|iPod|iPad/i).test(ua) && (/linux/i).test(pf))
|| (/ucweb.*linux/i.test(ua));
var isIOS = (/iPhone|iPod|iPad/i).test(ua) && !isAndroid;
var isWinPhone = (/Windows Phone|ZuneWP7/i).test(ua);
var clientWidth = document.documentElement.clientWidth;
if (!isAndroid && !isIOS && !isWinPhone && clientWidth > 1024) {
return false;
} else {
return true;
}
},
/**
* 监听
*/
listen: function () {
/**
* 清理
*/
$('body').on('click', '[data-clear]', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
sessionStorage.clear();
// 判断是否清理服务端
var clearUrl = $(this).attr('data-href');
if (clearUrl != undefined && clearUrl != '' && clearUrl != null) {
$.getJSON(clearUrl, function (data, status) {
layer.close(loading);
if (data.code != 0) {
return miniAdmin.error(data.msg);
} else {
return miniAdmin.success(data.msg);
}
}).fail(function () {
layer.close(loading);
return miniAdmin.error('清理缓存接口有误');
});
} else {
layer.close(loading);
return miniAdmin.success('清除缓存成功');
}
});
/**
* 刷新
*/
$('body').on('click', '[data-refresh]', function () {
$(".layui-tab-item.layui-show").find("iframe")[0].contentWindow.location.reload();
miniAdmin.success('刷新成功');
});
/**
* 监听提示信息
*/
$("body").on("mouseenter", ".layui-nav-tree .menu-li", function () {
if (miniAdmin.checkMobile()) {
return false;
}
var classInfo = $(this).attr('class'),
tips = $(this).prop("innerHTML"),
isShow = $('.layuimini-tool i').attr('data-side-fold');
if (isShow == 0 && tips) {
tips = "<ul class='layuimini-menu-left-zoom layui-nav layui-nav-tree layui-this'><li class='layui-nav-item layui-nav-itemed'>"+tips+"</li></ul>" ;
window.openTips = layer.tips(tips, $(this), {
tips: [2, '#2f4056'],
time: 300000,
skin:"popup-tips",
success:function (el) {
var left = $(el).position().left - 10 ;
$(el).css({ left:left });
element.render();
}
});
}
});
$("body").on("mouseleave", ".popup-tips", function () {
if (miniAdmin.checkMobile()) {
return false;
}
var isShow = $('.layuimini-tool i').attr('data-side-fold');
if (isShow == 0) {
try {
layer.close(window.openTips);
} catch (e) {
console.log(e.message);
}
}
});
/**
* 全屏
*/
$('body').on('click', '[data-check-screen]', function () {
var check = $(this).attr('data-check-screen');
if (check == 'full') {
miniAdmin.fullScreen();
$(this).attr('data-check-screen', 'exit');
$(this).html('<i class="layui-icon layui-icon-screen-restore"></i>');
} else {
miniAdmin.exitFullScreen();
$(this).attr('data-check-screen', 'full');
$(this).html('<i class="layui-icon layui-icon-screen-full"></i>');
}
});
/**
* 点击遮罩层
*/
$('body').on('click', '.layuimini-make', function () {
miniAdmin.renderDevice();
});
}
};
exports("miniAdmin", miniAdmin);
});
+253
View File
@@ -0,0 +1,253 @@
/**
* date:2020/02/27
* author:Mr.Chung
* version:2.0
* description:layuimini 菜单框架扩展
*/
layui.define(["element","laytpl" ,"jquery"], function (exports) {
var element = layui.element,
$ = layui.$,
laytpl = layui.laytpl,
layer = layui.layer;
var miniMenu = {
/**
* 菜单初始化
* @param options.menuList 菜单数据信息
* @param options.multiModule 是否开启多模块
* @param options.menuChildOpen 是否展开子菜单
*/
render: function (options) {
options.menuList = options.menuList || [];
options.multiModule = options.multiModule || false;
options.menuChildOpen = options.menuChildOpen || false;
if (options.multiModule) {
miniMenu.renderMultiModule(options.menuList, options.menuChildOpen);
} else {
miniMenu.renderSingleModule(options.menuList, options.menuChildOpen);
}
miniMenu.listen();
},
/**
* 单模块
* @param menuList 菜单数据
* @param menuChildOpen 是否默认展开
*/
renderSingleModule: function (menuList, menuChildOpen) {
menuList = menuList || [];
var leftMenuHtml = '',
childOpenClass = '',
leftMenuCheckDefault = 'layui-this';
var me = this ;
if (menuChildOpen) childOpenClass = ' layui-nav-itemed';
leftMenuHtml = this.renderLeftMenu(menuList,{ childOpenClass:childOpenClass }) ;
$('.layui-layout-body').addClass('layuimini-single-module'); //单模块标识
$('.layuimini-header-menu').remove();
$('.layuimini-menu-left').html(leftMenuHtml);
element.init();
},
/**
* 渲染一级菜单
*/
compileMenu: function(menu,isSub){
var menuHtml = '<li {{#if( d.menu){ }} data-menu="{{d.menu}}" {{#}}} class="layui-nav-item menu-li {{d.childOpenClass}} {{d.className}}" {{#if( d.id){ }} id="{{d.id}}" {{#}}}> <a href="javascript:;" {{#if( d.menu){ }} data-menu="{{d.menu}}" {{#}}} {{#if( d.dizhi){ }} layuimini-href="{{d.dizhi}}" {{#}}} {{#if( d.target){ }} target="{{d.target}}" {{#}}}>{{#if( d.icon){ }} <i class="layui-icon layui-icon-{{d.icon}}"></i> {{#}}} <span class="layui-left-nav {{-d.title}}">{{-d.title}}</span></a> {{# if(d.children){}} {{-d.children}} {{#}}} </li>' ;
if(isSub){
menuHtml = '<dd class="menu-dd {{d.childOpenClass}} {{ d.className }}"> <a href="javascript:;" {{#if( d.menu){ }} data-menu="{{d.menu}}" {{#}}} {{#if( d.id){ }} id="{{d.id}}" {{#}}} {{#if(( !d.children || !d.children.length ) && d.dizhi){ }} layuimini-href="{{d.dizhi}}" {{#}}} {{#if( d.target){ }} target="{{d.target}}" {{#}}}> {{#if( d.icon){ }} <i class="layui-icon layui-icon-{{d.icon}}"></i> {{#}}} <span class="layui-left-nav {{-d.title}}"> {{-d.title}}</span></a> {{# if(d.children){}} {{-d.children}} {{#}}}</dd>'
}
return laytpl(menuHtml).render(menu);
},
compileMenuContainer :function(menu,isSub){
var wrapperHtml = '<ul class="layui-nav layui-nav-tree layui-left-nav-tree {{d.className}}" id="{{d.id}}">{{-d.children}}</ul>' ;
if(isSub){
wrapperHtml = '<dl class="layui-nav-child">{{-d.children}}</dl>' ;
}
if(!menu.children){
return "";
}
return laytpl(wrapperHtml).render(menu);
},
each:function(list,callback){
var _list = [];
for(var i = 0 ,length = list.length ; i<length ;i++ ){
_list[i] = callback(i,list[i]) ;
}
return _list ;
},
renderChildrenMenu:function(menuList,options){
var me = this ;
menuList = menuList || [] ;
// console.log("menuList",menuList);
var html = this.each(menuList,function (idx,menu) {
if(menu.children && menu.children.length){
menu.children = me.renderChildrenMenu(menu.children,{ childOpenClass: options.childOpenClass || '' });
}
menu.className = "" ;
menu.childOpenClass = options.childOpenClass || ''
return me.compileMenu(menu,true)
}).join("");
return me.compileMenuContainer({ children:html },true)
},
renderLeftMenu :function(leftMenus,options){
options = options || {};
var me = this ;
var leftMenusHtml = me.each(leftMenus || [],function (idx,leftMenu) { // 左侧菜单遍历
var children = me.renderChildrenMenu(leftMenu.children, { childOpenClass:options.childOpenClass });
var leftMenuHtml = me.compileMenu({
href: leftMenu.href,
dizhi: leftMenu.dizhi,
id:leftMenu.id,
target: leftMenu.target,
childOpenClass: options.childOpenClass,
icon: leftMenu.icon,
title: leftMenu.title,
children: children,
className: '',
});
return leftMenuHtml ;
}).join("");
leftMenusHtml = me.compileMenuContainer({ id:options.parentMenuId,className:options.leftMenuCheckDefault,children:leftMenusHtml }) ;
return leftMenusHtml ;
},
/**
* 多模块
* @param menuList 菜单数据
* @param menuChildOpen 是否默认展开
*/
renderMultiModule: function (menuList, menuChildOpen) {
menuList = menuList || [];
var me = this ;
var headerMenuHtml = '',
headerMobileMenuHtml = '',
leftMenuHtml = '',
leftMenuCheckDefault = 'layui-this',
childOpenClass = '',
headerMenuCheckDefault = 'layui-this';
if (menuChildOpen) childOpenClass = ' layui-nav-itemed';
var headerMenuHtml = this.each(menuList, function (index, val) { //顶部菜单渲染
var menu = 'multi_module_' + index ;
var id = menu+"HeaderId";
var topMenuItemHtml = "" ;
topMenuItemHtml = me.compileMenu({
className:headerMenuCheckDefault,
menu:menu,
id:id,
title:val.title,
href:"",
target:"",
children:""
});
leftMenuHtml+=me.renderLeftMenu(val.children,{
parentMenuId:menu,
childOpenClass:childOpenClass,
leftMenuCheckDefault:leftMenuCheckDefault
});
headerMobileMenuHtml +=me.compileMenu({ id:id,menu:menu,id:id,icon:val.icon, title:val.title, },true);
headerMenuCheckDefault = "";
leftMenuCheckDefault = "layui-hide" ;
return topMenuItemHtml ;
}).join("");
$('.layui-layout-body').addClass('layuimini-multi-module'); //多模块标识
$('.layuimini-menu-header-pc').html(headerMenuHtml); //电脑
$('.layuimini-menu-left').html(leftMenuHtml);
$('.layuimini-menu-header-mobile').html(headerMobileMenuHtml); //手机
element.init();
},
/**
* 监听
*/
listen: function () {
/**
* 菜单模块切换
*/
$('body').on('click', '[data-menu]', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var menuId = $(this).attr('data-menu');
// header
$(".layuimini-header-menu .layui-nav-item.layui-this").removeClass('layui-this');
$(this).addClass('layui-this');
// left
$(".layuimini-menu-left .layui-nav.layui-nav-tree.layui-this").addClass('layui-hide');
$(".layuimini-menu-left .layui-nav.layui-nav-tree.layui-this.layui-hide").removeClass('layui-this');
$("#" + menuId).removeClass('layui-hide');
$("#" + menuId).addClass('layui-this');
layer.close(loading);
});
/**
* 菜单缩放
*/
$('body').on('click', '.layuimini-site-mobile', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var isShow = $('.layuimini-tool [data-side-fold]').attr('data-side-fold');
if (isShow == 1) { // 缩放
$('.layuimini-tool [data-side-fold]').attr('data-side-fold', 0);
$('.layuimini-tool [data-side-fold]').attr('class', 'layui-icon layui-icon-spread-left');
$('.layui-layout-body').removeClass('layuimini-all');
$('.layui-layout-body').addClass('layuimini-mini');
} else { // 正常
$('.layuimini-tool [data-side-fold]').attr('data-side-fold', 1);
$('.layuimini-tool [data-side-fold]').attr('class', 'layui-icon layui-icon-shrink-right');
$('.layui-layout-body').removeClass('layuimini-mini');
$('.layui-layout-body').addClass('layuimini-all');
layer.close(window.openTips);
}
element.init();
layer.close(loading);
});
/**
* 菜单缩放
*/
$('body').on('click', '[data-side-fold]', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var isShow = $('.layuimini-tool [data-side-fold]').attr('data-side-fold');
if (isShow == 1) { // 缩放
$('.layuimini-tool [data-side-fold]').attr('data-side-fold', 0);
$('.layuimini-tool [data-side-fold]').attr('class', 'layui-icon layui-icon-spread-left');
$('.layui-layout-body').removeClass('layuimini-all');
$('.layui-layout-body').addClass('layuimini-mini');
// $(".menu-li").each(function (idx,el) {
// $(el).addClass("hidden-sub-menu");
// });
} else { // 正常
$('.layuimini-tool [data-side-fold]').attr('data-side-fold', 1);
$('.layuimini-tool [data-side-fold]').attr('class', 'layui-icon layui-icon-shrink-right');
$('.layui-layout-body').removeClass('layuimini-mini');
$('.layui-layout-body').addClass('layuimini-all');
// $(".menu-li").each(function (idx,el) {
// $(el).removeClass("hidden-sub-menu");
// });
layer.close(window.openTips);
}
element.init();
layer.close(loading);
});
/**
* 手机端点开模块
*/
$('body').on('click', '.layuimini-header-menu.layuimini-mobile-show dd', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var check = $('.layuimini-tool [data-side-fold]').attr('data-side-fold');
if(check === "1"){
$('.layuimini-site-mobile').trigger("click");
element.init();
}
layer.close(loading);
});
},
};
exports("miniMenu", miniMenu);
});
+676
View File
@@ -0,0 +1,676 @@
/**
* date:2020/02/27
* author:Mr.Chung
* version:2.0
* description:layuimini tab框架扩展
* 新增功能: 点击左侧菜单,如果标签存在则刷新,不存在则新建
* 修改bug: 打开多个标签时,点击关闭当前标签时会把当前标签及后面的标签全部关闭的bug,364行
*/
layui.define(["element", "layer", "jquery","tabs"], function (exports) {
var element = layui.element,
tabs=layui.tabs,
layer = layui.layer,
$ = layui.$;
var miniTab = {
/**
* 初始化tab
* @param options
*/
render: function (options) {
options.filter = options.filter || null;
options.multiModule = options.multiModule || false;
options.urlHashLocation = options.urlHashLocation || false;
options.maxTabNum = options.maxTabNum || 200;
options.menuList = options.menuList || []; // todo 后期菜单想改为不操作dom, 而是直接操作初始化传过来的数据
options.homeInfo = options.homeInfo || {};
options.listenSwichCallback = options.listenSwichCallback || function () {
};
miniTab.listen(options);
miniTab.listenRoll();
miniTab.listenSwitch(options);
miniTab.listenHash(options);
},
/**
* 新建tab窗口
* @param options.tabId
* @param options.href
* @param options.title
* @param options.isIframe
* @param options.maxTabNum
*/
create: function (options) {
options.href = options.href || null;
options.tabId = options.tabId || options.href;
options.title = options.title || null;
options.isIframe = options.isIframe || false;
options.maxTabNum = options.maxTabNum || 200;
if ($(".layuimini-tab .layui-tab-title li").length >= options.maxTabNum) {
layer.msg('Tab窗口已达到限定数量,请先关闭部分Tab');
return false;
}
var ele = element;
if (options.isIframe) ele = parent.layui.element;
//
console.log('aaa');
ele.tabAdd('layuiminiTab', {
title: '<span class="layuimini-tab-active"></span><span>' + options.title + '</span><i class="layui-icon layui-unselect layui-tab-close">ဆ</i>' //用于演示
, content: '<iframe width="100%" height="100%" frameborder="no" border="0" marginwidth="0" marginheight="0" id="'+options.tabId+'" src="' + options.href + '"></iframe>'
, id: options.tabId
});
console.log('bbb');
//
$('.layuimini-menu-left').attr('layuimini-tab-tag', 'add');
sessionStorage.setItem('layuiminimenu_' + options.tabId, options.title);
},
/**
* 切换选项卡
* @param tabId
*/
change: function (tabId) {
element.tabChange('layuiminiTab', tabId);
},
/**
* 删除tab窗口
* @param tabId
* @param isParent
*/
delete: function (tabId, isParent) {
// todo 未知BUG,不知道是不是layui问题,必须先删除元素
$(".layuimini-tab .layui-tab-title .layui-unselect.layui-tab-bar").remove();
if (isParent === true) {
parent.layui.element.tabDelete('layuiminiTab', tabId);
} else {
element.tabDelete('layuiminiTab', tabId);
}
},
/**
* 在iframe层打开新tab方法
*/
openNewTabByIframe: function (options) {
options.href = options.href || null;
options.id = options.id || options.href;
options.title = options.title || null;
var loading = parent.layer.load(0, {shade: false, time: 2 * 1000});
if (options.href === null || options.href === undefined) options.href = new Date().getTime();
var checkTab = miniTab.check(options.id, true);
if (!checkTab) {
parent.layui.element.tabChange('layuiminiTab','0');
setTimeout(function(){
miniTab.create({
tabId: options.id,
href: options.href,
title: options.title,
isIframe: true,
});
parent.layui.element.tabChange('layuiminiTab', options.id);
parent.layer.close(loading);
},100);
}else{
parent.layui.element.tabChange('layuiminiTab', options.id);
parent.layer.close(loading);
}
},
openNewTab: function (options) {
// console.log(options,'o');
options.href = options.href || null;
options.id = options.id || options.href;
options.title = options.title || null;
// console.log(options,'p');
var loading = layer.load(0, {shade: false, time: 2 * 1000});
if (options.href === null || options.href === undefined) options.href = new Date().getTime();
var checkTab = miniTab.check(options.id, true);
// console.log(checkTab,'po',options.id);
if (!checkTab) {
layui.element.tabChange('layuiminiTab','0');
setTimeout(() => {
miniTab.create({
tabId: options.id,
href: options.href,
title: options.title,
isIframe: true,
});
layui.element.tabChange('layuiminiTab', options.id);
layer.close(loading);
}, 100);
}else{
layui.element.tabChange('layuiminiTab', options.id);
layer.close(loading);
}
},
/**
* 在iframe层关闭当前tab方法
*/
deleteCurrentByIframe: function () {
var ele = $(".layuimini-tab .layui-tab-title li.layui-this", parent.document);
if (ele.length > 0) {
var layId = $(ele[0]).attr('lay-id');
miniTab.delete(layId, true);
}
},
/**
* 判断tab窗口
*/
check: function (tabId, isIframe) {
// 判断选项卡上是否有
tabId=tabId+"";
var checkTab = false;
if (isIframe === undefined || isIframe === false) {
$(".layui-tab-title li").each(function () {
var checkTabId = $(this).attr('lay-id');
if (checkTabId != null && checkTabId === tabId) {
checkTab = true;
}
});
} else {
parent.layui.$(".layui-tab-title li").each(function () {
var checkTabId = $(this).attr('lay-id');
if (checkTabId != null && checkTabId === tabId) {
checkTab = true;
}
});
}
return checkTab;
},
/**
* 开启tab右键菜单
* @param tabId
* @param left
*/
openTabRignMenu: function (tabId, left) {
miniTab.closeTabRignMenu();
var menuHtml = '<div class="layui-unselect layui-form-select layui-form-selected layuimini-tab-mousedown layui-show" data-tab-id="' + tabId + '" style="left: ' + left + 'px!important">\n' +
'<dl>\n' +
'<dd><a href="javascript:;" layuimini-tab-menu-close="current">关 闭 当 前</a></dd>\n' +
'<dd><a href="javascript:;" layuimini-tab-menu-close="other">关 闭 其 他</a></dd>\n' +
'<dd><a href="javascript:;" layuimini-tab-menu-close="all">关 闭 全 部</a></dd>\n' +
'<dd><a href="javascript:;" layuimini-tab-menu-close="divorced">脱 离 标 签</a></dd>\n' +
'</dl>\n' +
'</div>';
var makeHtml = '<div class="layuimini-tab-make"></div>';
$('.layuimini-tab .layui-tab-title').after(menuHtml);
$('.layuimini-tab .layui-tab-content').after(makeHtml);
},
/**
* 关闭tab右键菜单
*/
closeTabRignMenu: function () {
$('.layuimini-tab-mousedown').remove();
$('.layuimini-tab-make').remove();
},
/**
* 查询菜单信息
* @param href
* @param menuList
*/
searchMenu: function (href, menuList) {
var menu;
for (key in menuList) {
var item = menuList[key];
if (item.href === href) {
menu = item;
break;
}
if (item.child) {
newMenu = miniTab.searchMenu(href, item.child);
if (newMenu) {
menu = newMenu;
break;
}
}
}
return menu;
},
/**
* 监听
* @param options
*/
listen: function (options) {
options = options || {};
options.maxTabNum = options.maxTabNum || 200;
/**
* 打开新窗口
*/
$('body').on('click', '[layuimini-href]', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var tabId = $(this).attr('layuimini-href'),
href = $(this).attr('layuimini-href'),
title = $(this).text(),
target = $(this).attr('target');
var el = $("[layuimini-href='" + href + "']", ".layuimini-menu-left");
layer.close(window.openTips);
if (el.length) {
$(el).closest(".layui-nav-tree").find(".layui-this").removeClass("layui-this");
$(el).parent().addClass("layui-this");
}
if (target === '_blank') {
layer.close(loading);
window.open(href, "_blank");
return false;
}
if (tabId === null || tabId === undefined) tabId = new Date().getTime();
var checkTab = miniTab.check(tabId);
if(checkTab){
miniTab.change(tabId);
}
if (!checkTab) {
miniTab.create({
tabId: tabId,
href: href,
title: title,
isIframe: false,
maxTabNum: options.maxTabNum,
});
}
element.tabChange('layuiminiTab', tabId);
layer.close(loading);
});
/**
* 在iframe子菜单上打开新窗口
*/
$('body').on('click', '[layuimini-content-href]', function () {
var loading = parent.layer.load(0, {shade: false, time: 2 * 1000});
var tabId = $(this).attr('layuimini-content-href'),
href = $(this).attr('layuimini-content-href'),
title = $(this).attr('data-title'),
target = $(this).attr('target');
if (target === '_blank') {
parent.layer.close(loading);
window.open(href, "_blank");
return false;
}
if (tabId === null || tabId === undefined) tabId = new Date().getTime();
var checkTab = miniTab.check(tabId, true);
if (!checkTab) {
miniTab.create({
tabId: tabId,
href: href,
title: title,
isIframe: true,
maxTabNum: options.maxTabNum,
});
}
parent.layui.element.tabChange('layuiminiTab', tabId);
parent.layer.close(loading);
});
/**
* 关闭选项卡
**/
$('body').on('click', '.layuimini-tab .layui-tab-title .layui-tab-close', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var $parent = $(this).parent();
var tabId = $parent.attr('lay-id');
if (tabId !== undefined || tabId !== null) {
miniTab.delete(tabId);
}
layer.close(loading);
});
/**
* 选项卡操作
*/
$('body').on('click', '[layuimini-tab-close]', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var closeType = $(this).attr('layuimini-tab-close');
$(".layuimini-tab .layui-tab-title li").each(function () {
var tabId = $(this).attr('lay-id');
var id = $(this).attr('id');
var isCurrent = $(this).hasClass('layui-this');
if (id !== 'layuiminiHomeTabId') {
if (closeType === 'all') {
miniTab.delete(tabId);
} else {
if (closeType === 'current' && isCurrent) {
miniTab.delete(tabId);
} else if (closeType === 'other' && !isCurrent) {
miniTab.delete(tabId);
}
}
}
});
layer.close(loading);
});
/**
* 禁用网页右键
*/
$(".layuimini-tab .layui-tab-title").unbind("mousedown").bind("contextmenu", function (e) {
e.preventDefault();
return false;
});
/**
* 注册鼠标右键
*/
$('body').on('mousedown', '.layuimini-tab .layui-tab-title li', function (e) {
var left = $(this).offset().left - $('.layuimini-tab ').offset().left + ($(this).width() / 2),
tabId = $(this).attr('lay-id');
if (e.which === 3) {
miniTab.openTabRignMenu(tabId, left);
}
});
/**
* 关闭tab右键菜单
*/
$('body').on('click', '.layui-body,.layui-header,.layuimini-menu-left,.layuimini-tab-make', function () {
miniTab.closeTabRignMenu();
});
/**
* tab右键选项卡操作
*/
$('body').on('click', '[layuimini-tab-menu-close]', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var closeType = $(this).attr('layuimini-tab-menu-close'),
currentTabId = $('.layuimini-tab-mousedown').attr('data-tab-id');
$(".layuimini-tab .layui-tab-title li").each(function () {
var tabId = $(this).attr('lay-id');
var id = $(this).attr('id');
if (id !== 'layuiminiHomeTabId') {
if (closeType === 'all') {
miniTab.delete(tabId);
} else {
if (closeType === 'current' && currentTabId === tabId) {
miniTab.delete(tabId);
return false;
} else if (closeType === 'other' && currentTabId !== tabId) {
miniTab.delete(tabId);
} else if (closeType === 'divorced' && currentTabId === tabId) {
miniTab.divorced(tabId);
}
}
}
});
miniTab.closeTabRignMenu();
layer.close(loading);
});
},
/**
* 监听tab切换
* @param options
*/
listenSwitch: function (options) {
options.filter = options.filter || null;
options.multiModule = options.multiModule || false;
options.urlHashLocation = options.urlHashLocation || false;
options.listenSwichCallback = options.listenSwichCallback || function () {
};
element.on('tab(' + options.filter + ')', function (data) {
var tabId = $(this).attr('lay-id');
if (options.urlHashLocation) {
location.hash = '/' + tabId;
}
if (typeof options.listenSwichCallback === 'function') {
options.listenSwichCallback();
}
if (typeof options.switchtoindex === 'function') {
options.switchtoindex();
}
// 判断是否为新增窗口
if ($('.layuimini-menu-left').attr('layuimini-tab-tag') === 'add') {
$('.layuimini-menu-left').attr('layuimini-tab-tag', 'no')
}
$("div.layui-side ul li.layui-nav-itemed").removeClass("layui-nav-itemed");
$("[layuimini-href]").parent().removeClass('layui-this');
if (options.multiModule) {
miniTab.listenSwitchMultiModule(tabId);
} else {
miniTab.listenSwitchSingleModule(tabId);
}
miniTab.rollPosition();
});
},
/**
* 监听hash变化
* @param options
* @returns {boolean}
*/
listenHash: function (options) {
options.urlHashLocation = options.urlHashLocation || false;
options.maxTabNum = options.maxTabNum || 200;
options.homeInfo = options.homeInfo || {};
options.menuList = options.menuList || [];
if (!options.urlHashLocation) return false;
var tabId = location.hash.replace(/^#\//, '');
if (tabId === null || tabId === undefined || tabId ==='') return false;
// 判断是否为首页
if(tabId ===options.homeInfo.href) return false;
// 判断是否为右侧菜单
var menu = miniTab.searchMenu(tabId, options.menuList);
if (menu !== undefined) {
miniTab.create({
tabId: tabId,
href: tabId,
title: menu.title,
isIframe: false,
maxTabNum: options.maxTabNum,
});
$('.layuimini-menu-left').attr('layuimini-tab-tag', 'no');
element.tabChange('layuiminiTab', tabId);
return false;
}
// 判断是否为快捷菜单
var isSearchMenu = false;
$("[layuimini-content-href]").each(function () {
if ($(this).attr("layuimini-content-href") === tabId) {
var title = $(this).attr("data-title");
miniTab.create({
tabId: tabId,
href: tabId,
title: title,
isIframe: false,
maxTabNum: options.maxTabNum,
});
$('.layuimini-menu-left').attr('layuimini-tab-tag', 'no');
element.tabChange('layuiminiTab', tabId);
isSearchMenu = true;
return false;
}
});
if (isSearchMenu) return false;
// 既不是右侧菜单、快捷菜单,就直接打开
var title = sessionStorage.getItem('layuiminimenu_' + tabId) === null ? tabId : sessionStorage.getItem('layuiminimenu_' + tabId);
miniTab.create({
tabId: tabId,
href: tabId,
title: title,
isIframe: false,
maxTabNum: options.maxTabNum,
});
element.tabChange('layuiminiTab', tabId);
return false;
},
/**
* 监听滚动
*/
listenRoll: function () {
$(".layuimini-tab-roll-left").click(function () {
miniTab.rollClick("left");
});
$(".layuimini-tab-roll-right").click(function () {
miniTab.rollClick("right");
});
},
/**
* 单模块切换
* @param tabId
*/
listenSwitchSingleModule: function (tabId) {
$("[layuimini-href]").each(function () {
if ($(this).attr("layuimini-href") === tabId) {
// 自动展开菜单栏
var addMenuClass = function ($element, type) {
if (type === 1) {
$element.addClass('layui-this');
if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-this')) {
$(".layuimini-header-menu li").attr('class', 'layui-nav-item');
} else {
addMenuClass($element.parent().parent(), 2);
}
} else {
$element.addClass('layui-nav-itemed');
if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-nav-itemed')) {
$(".layuimini-header-menu li").attr('class', 'layui-nav-item');
} else {
addMenuClass($element.parent().parent(), 2);
}
}
};
addMenuClass($(this).parent(), 1);
return false;
}
});
},
/**
* 多模块切换
* @param tabId
*/
listenSwitchMultiModule: function (tabId) {
$("[layuimini-href]").each(function () {
if ($(this).attr("layuimini-href") === tabId) {
// 自动展开菜单栏
var addMenuClass = function ($element, type) {
if (type === 1) {
$element.addClass('layui-this');
if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-this')) {
var moduleId = $element.parent().attr('id');
$(".layuimini-header-menu li").attr('class', 'layui-nav-item');
$("#" + moduleId + "HeaderId").addClass("layui-this");
$(".layuimini-menu-left .layui-nav.layui-nav-tree").attr('class', 'layui-nav layui-nav-tree layui-hide');
$("#" + moduleId).attr('class', 'layui-nav layui-nav-tree layui-this');
} else {
addMenuClass($element.parent().parent(), 2);
}
} else {
$element.addClass('layui-nav-itemed');
if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-nav-itemed')) {
var moduleId = $element.parent().attr('id');
$(".layuimini-header-menu li").attr('class', 'layui-nav-item');
$("#" + moduleId + "HeaderId").addClass("layui-this");
$(".layuimini-menu-left .layui-nav.layui-nav-tree").attr('class', 'layui-nav layui-nav-tree layui-hide');
$("#" + moduleId).attr('class', 'layui-nav layui-nav-tree layui-this');
} else {
addMenuClass($element.parent().parent(), 2);
}
}
};
addMenuClass($(this).parent(), 1);
return false;
}
});
},
/**
* 自动定位
*/
rollPosition: function () {
var $tabTitle = $('.layuimini-tab .layui-tab-title');
var autoLeft = 0;
$tabTitle.children("li").each(function () {
if ($(this).hasClass('layui-this')) {
return false;
} else {
autoLeft += $(this).outerWidth();
}
});
$tabTitle.animate({
scrollLeft: autoLeft - $tabTitle.width() / 3
}, 200);
},
/**
* 点击滚动
* @param direction
*/
rollClick: function (direction) {
var $tabTitle = $('.layuimini-tab .layui-tab-title');
var left = $tabTitle.scrollLeft();
if ('left' === direction) {
$tabTitle.animate({
scrollLeft: left - 450
}, 200);
} else {
$tabTitle.animate({
scrollLeft: left + 450
}, 200);
}
},
/**
* 脱离标签栏
* @param tabId
*/
divorced: function (tabId) {
let tabtitle = $("ul.layui-tab-title").children('li[lay-id="' + tabId + '"]');
let title = tabtitle.children("span").text();
let tab = $("div.layui-tab-item").children("iframe[id='" + tabId + "']");
let id = tabId.replace(/[^\u4e00-\u9fa5\w]/g, "");
layer.open({
id: id,
title: title,
type: 1,
content: "",
shadeClose: false,
shade: 0,
maxmin: true,
area: ['50%', '80%'],
success: function (layero, index) {
//layero tab
tabtitle.hide();
tabtitle.removeClass("layui-this");
tab.parent("div.layui-tab-item").attr("layui-id", index);
tab.appendTo($(layero).children("div#" + id));
//$(layero).children("div#" + id)[0].addendChild(tab[0]);
},
cancel: function (index, layero) {
let iframe = $(layero).children("div#" + id).children("iframe");
iframe.appendTo($("div.layui-tab-item[layui-id=" + index + "]"));
//$("div.layui-tab-item.layui-show")[0].addendChild(iframe[0]);
tabtitle.addClass("layui-this");
tabtitle.show();
},
end: function () {
layer.msg("已关闭");
}
});
}
};
exports("miniTab", miniTab);
});
+469
View File
@@ -0,0 +1,469 @@
/**
* date:2020/02/28
* author:Mr.Chung
* version:2.0
* description:layuimini tab框架扩展
*/
layui.define(["jquery", "layer"], function (exports) {
var $ = layui.$,
layer = layui.layer;
var miniTheme = {
/**
* 主题配置项
* @param bgcolorId
* @returns {{headerLogo, menuLeftHover, headerRight, menuLeft, headerRightThis, menuLeftThis}|*|*[]}
*/
config: function (bgcolorId) {
var bgColorConfig = [
{
headerRightBg: '#ffffff', //头部右侧背景色
headerRightBgThis: '#e4e4e4', //头部右侧选中背景色,
headerRightColor: 'rgba(107, 107, 107, 0.7)', //头部右侧字体颜色,
headerRightChildColor: 'rgba(107, 107, 107, 0.7)', //头部右侧下拉字体颜色,
headerRightColorThis: '#565656', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(160, 160, 160, 0.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#247AE0', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#565656', //头部缩放按钮样式,
headerLogoBg: '#192027', //logo背景颜色,
headerLogoColor: 'rgb(191, 187, 187)', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#28333E', //左侧菜单背景,
leftMenuBgThis: '#247AE0', //左侧菜单选中背景,
leftMenuChildBg: '#0c0f13', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#247AE0', //tab选项卡选中颜色,
},
{
headerRightBg: '#23262e', //头部右侧背景色
headerRightBgThis: '#0c0c0c', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#1aa094', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#0c0c0c', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#23262e', //左侧菜单背景,
leftMenuBgThis: '#737373', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#23262e', //tab选项卡选中颜色,
},
{
headerRightBg: '#ffa4d1', //头部右侧背景色
headerRightBgThis: '#bf7b9d', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#ffa4d1', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#e694bd', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#1f1f1f', //左侧菜单背景,
leftMenuBgThis: '#737373', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#ffa4d1', //tab选项卡选中颜色,
},
{
headerRightBg: '#1aa094', //头部右侧背景色
headerRightBgThis: '#197971', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#1aa094', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#0c0c0c', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#23262e', //左侧菜单背景,
leftMenuBgThis: '#1aa094', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#1aa094', //tab选项卡选中颜色,
},
{
headerRightBg: '#1e9fff', //头部右侧背景色
headerRightBgThis: '#0069b7', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#1e9fff', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#0c0c0c', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#1f1f1f', //左侧菜单背景,
leftMenuBgThis: '#1e9fff', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#1e9fff', //tab选项卡选中颜色,
},
{
headerRightBg: '#ffb800', //头部右侧背景色
headerRightBgThis: '#d09600', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#d09600', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#243346', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#2f4056', //左侧菜单背景,
leftMenuBgThis: '#8593a7', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#ffb800', //tab选项卡选中颜色,
},
{
headerRightBg: '#e82121', //头部右侧背景色
headerRightBgThis: '#ae1919', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#ae1919', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#0c0c0c', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#1f1f1f', //左侧菜单背景,
leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#e82121', //tab选项卡选中颜色,
},
{
headerRightBg: '#963885', //头部右侧背景色
headerRightBgThis: '#772c6a', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#772c6a', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#243346', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#2f4056', //左侧菜单背景,
leftMenuBgThis: '#586473', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#963885', //tab选项卡选中颜色,
},
{
headerRightBg: '#2D8CF0', //头部右侧背景色
headerRightBgThis: '#0069b7', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#0069b7', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#0069b7', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#1f1f1f', //左侧菜单背景,
leftMenuBgThis: '#2D8CF0', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#2d8cf0', //tab选项卡选中颜色,
},
{
headerRightBg: '#ffb800', //头部右侧背景色
headerRightBgThis: '#d09600', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#d09600', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#d09600', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#2f4056', //左侧菜单背景,
leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#ffb800', //tab选项卡选中颜色,
},
{
headerRightBg: '#e82121', //头部右侧背景色
headerRightBgThis: '#ae1919', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#ae1919', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#d91f1f', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#1f1f1f', //左侧菜单背景,
leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#e82121', //tab选项卡选中颜色,
},
{
headerRightBg: '#963885', //头部右侧背景色
headerRightBgThis: '#772c6a', //头部右侧选中背景色,
headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色,
headerRightChildColor: '#676767', //头部右侧下拉字体颜色,
headerRightColorThis: '#ffffff', //头部右侧鼠标选中,
headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色,
headerRightNavMoreBg: '#772c6a', //头部右侧更多下拉列表选中背景色,
headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色,
headerRightToolColor: '#bbe3df', //头部缩放按钮样式,
headerLogoBg: '#772c6a', //logo背景颜色,
headerLogoColor: '#ffffff', //logo字体颜色,
leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式,
leftMenuBg: '#2f4056', //左侧菜单背景,
leftMenuBgThis: '#626f7f', //左侧菜单选中背景,
leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景,
leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色,
leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色,
tabActiveColor: '#963885', //tab选项卡选中颜色,
}
];
if (bgcolorId === undefined) {
return bgColorConfig;
} else {
return bgColorConfig[bgcolorId];
}
},
/**
* 初始化
* @param options
*/
render: function (options) {
options.bgColorDefault = options.bgColorDefault || false;
options.listen = options.listen || false;
var bgcolorId = sessionStorage.getItem('layuiminiBgcolorId');
if (bgcolorId === null || bgcolorId === undefined || bgcolorId === '') {
bgcolorId = options.bgColorDefault;
}
miniTheme.buildThemeCss(bgcolorId);
if (options.listen) miniTheme.listen(options);
},
/**
* 构建主题样式
* @param bgcolorId
* @returns {boolean}
*/
buildThemeCss: function (bgcolorId) {
if (!bgcolorId) {
return false;
}
var bgcolorData = miniTheme.config(bgcolorId);
var styleHtml = '/*头部右侧背景色 headerRightBg */\n' +
'.layui-layout-admin .layui-header {\n' +
' background-color: ' + bgcolorData.headerRightBg + ' !important;\n' +
'}\n' +
'\n' +
'/*头部右侧选中背景色 headerRightBgThis */\n' +
'.layui-layout-admin .layui-header .layuimini-header-content > ul > .layui-nav-item.layui-this, .layuimini-tool i:hover {\n' +
' background-color: ' + bgcolorData.headerRightBgThis + ' !important;\n' +
'}\n' +
'\n' +
'/*头部右侧字体颜色 headerRightColor */\n' +
'.layui-layout-admin .layui-header .layui-nav .layui-nav-item a {\n' +
' color: ' + bgcolorData.headerRightColor + ';\n' +
'}\n' +
'/**头部右侧下拉字体颜色 headerRightChildColor */\n' +
'.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child a {\n' +
' color: ' + bgcolorData.headerRightChildColor + '!important;\n' +
'}\n'+
'\n' +
'/*头部右侧鼠标选中 headerRightColorThis */\n' +
'.layui-header .layuimini-menu-header-pc.layui-nav .layui-nav-item a:hover, .layui-header .layuimini-header-menu.layuimini-pc-show.layui-nav .layui-this a {\n' +
' color: ' + bgcolorData.headerRightColorThis + ' !important;\n' +
'}\n' +
'\n' +
'/*头部右侧更多下拉颜色 headerRightNavMore */\n' +
'.layui-header .layui-nav .layui-nav-more {\n' +
' border-top-color: ' + bgcolorData.headerRightNavMore + ' !important;\n' +
'}\n' +
'\n' +
'/*头部右侧更多下拉颜色 headerRightNavMore */\n' +
'.layui-header .layui-nav .layui-nav-mored, .layui-header .layui-nav-itemed > a .layui-nav-more {\n' +
' border-color: transparent transparent ' + bgcolorData.headerRightNavMore + ' !important;\n' +
'}\n' +
'\n' +
'/**头部右侧更多下拉配置色 headerRightNavMoreBg headerRightNavMoreColor */\n' +
'.layui-header .layui-nav .layui-nav-child dd.layui-this a, .layui-header .layui-nav-child dd.layui-this, .layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this a {\n' +
' background-color: ' + bgcolorData.headerRightNavMoreBg + ' !important;\n' +
' color:' + bgcolorData.headerRightNavMoreColor + ' !important;\n' +
'}\n' +
'\n' +
'/*头部缩放按钮样式 headerRightToolColor */\n' +
'.layui-layout-admin .layui-header .layuimini-tool i {\n' +
' color: ' + bgcolorData.headerRightToolColor + ';\n' +
'}\n' +
'\n' +
'/*logo背景颜色 headerLogoBg */\n' +
'.layui-layout-admin .layuimini-logo {\n' +
' background-color: ' + bgcolorData.headerLogoBg + ' !important;\n' +
'}\n' +
'\n' +
'/*logo字体颜色 headerLogoColor */\n' +
'.layui-layout-admin .layuimini-logo h1 {\n' +
' color: ' + bgcolorData.headerLogoColor + ';\n' +
'}\n' +
'\n' +
'/*左侧菜单更多下拉样式 leftMenuNavMore */\n' +
'.layuimini-menu-left .layui-nav .layui-nav-more,.layuimini-menu-left-zoom.layui-nav .layui-nav-more {\n' +
' border-top-color: ' + bgcolorData.leftMenuNavMore + ';\n' +
'}\n' +
'\n' +
'/*左侧菜单更多下拉样式 leftMenuNavMore */\n' +
'.layuimini-menu-left .layui-nav .layui-nav-mored, .layuimini-menu-left .layui-nav-itemed > a .layui-nav-more, .layuimini-menu-left-zoom.layui-nav .layui-nav-mored, .layuimini-menu-left-zoom.layui-nav-itemed > a .layui-nav-more {\n' +
' border-color: transparent transparent ' + bgcolorData.leftMenuNavMore + ' !important;\n' +
'}\n' +
'\n' +
'/*左侧菜单背景 leftMenuBg */\n' +
'.layui-side.layui-bg-black, .layui-side.layui-bg-black > .layuimini-menu-left > ul, .layuimini-menu-left-zoom > ul {\n' +
' background-color: ' + bgcolorData.leftMenuBg + ' !important;\n' +
'}\n' +
'\n' +
'/*左侧菜单选中背景 leftMenuBgThis */\n' +
'.layuimini-menu-left .layui-nav-tree .layui-this, .layuimini-menu-left .layui-nav-tree .layui-this > a, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a, .layuimini-menu-left-zoom.layui-nav-tree .layui-this, .layuimini-menu-left-zoom.layui-nav-tree .layui-this > a, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this a {\n' +
' background-color: ' + bgcolorData.leftMenuBgThis + ' !important\n' +
'}\n' +
'\n' +
'/*左侧菜单子菜单背景 leftMenuChildBg */\n' +
'.layuimini-menu-left .layui-nav-itemed > .layui-nav-child{\n' +
' background-color: ' + bgcolorData.leftMenuChildBg + ' !important;\n' +
'}\n' +
'\n' +
'/*左侧菜单字体颜色 leftMenuColor */\n' +
'.layuimini-menu-left .layui-nav .layui-nav-item a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a {\n' +
' color: ' + bgcolorData.leftMenuColor + ' !important;\n' +
'}\n' +
'\n' +
'/*左侧菜单选中字体颜色 leftMenuColorThis */\n' +
'.layuimini-menu-left .layui-nav .layui-nav-item a:hover, .layuimini-menu-left .layui-nav .layui-this a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a:hover, .layuimini-menu-left-zoom.layui-nav .layui-this a {\n' +
' color:' + bgcolorData.leftMenuColorThis + ' !important;\n' +
'}\n' +
'\n' +
'/**tab选项卡选中颜色 tabActiveColor */\n' +
'.layuimini-tab .layui-tab-title .layui-this .layuimini-tab-active {\n' +
' background-color: ' + bgcolorData.tabActiveColor + ';\n' +
'}\n';
$('#layuimini-bg-color').html(styleHtml);
},
/**
* 构建主题选择html
* @param options
* @returns {string}
*/
buildBgColorHtml: function (options) {
options.bgColorDefault = options.bgColorDefault || 0;
var bgcolorId = parseInt(sessionStorage.getItem('layuiminiBgcolorId'));
if (isNaN(bgcolorId)) bgcolorId = options.bgColorDefault;
var bgColorConfig = miniTheme.config();
var html = '';
$.each(bgColorConfig, function (key, val) {
if (key === bgcolorId) {
html += '<li class="layui-this" data-select-bgcolor="' + key + '">\n';
} else {
html += '<li data-select-bgcolor="' + key + '">\n';
}
html += '<a href="javascript:;" data-skin="skin-blue" style="" class="clearfix full-opacity-hover">\n' +
'<div><span style="display:block; width: 20%; float: left; height: 12px; background: ' + val.headerLogoBg + ';"></span><span style="display:block; width: 80%; float: left; height: 12px; background: ' + val.headerRightBg + ';"></span></div>\n' +
'<div><span style="display:block; width: 20%; float: left; height: 40px; background: ' + val.leftMenuBg + ';"></span><span style="display:block; width: 80%; float: left; height: 40px; background: #ffffff;"></span></div>\n' +
'</a>\n' +
'</li>';
});
return html;
},
/**
* 监听
* @param options
*/
listen: function (options) {
$('body').on('click', '[data-bgcolor]', function () {
var loading = layer.load(0, {shade: false, time: 2 * 1000});
var clientHeight = (document.documentElement.clientHeight) - 60;
var bgColorHtml = miniTheme.buildBgColorHtml(options);
var html = '<div class="layuimini-color">\n' +
'<div class="color-title">\n' +
'<span>配色方案</span>\n' +
'</div>\n' +
'<div class="color-content">\n' +
'<ul>\n' + bgColorHtml + '</ul>\n' +
'</div>\n' +
'</div>';
layer.open({
type: 1,
title: false,
closeBtn: 0,
shade: 0.2,
anim: 2,
shadeClose: true,
id: 'layuiminiBgColor',
area: ['340px', clientHeight + 'px'],
offset: 'rb',
content: html,
success: function (index, layero) {
},
end: function () {
$('.layuimini-select-bgcolor').removeClass('layui-this');
}
});
layer.close(loading);
});
$('body').on('click', '[data-select-bgcolor]', function () {
var bgcolorId = $(this).attr('data-select-bgcolor');
$('.layuimini-color .color-content ul .layui-this').attr('class', '');
$(this).attr('class', 'layui-this');
sessionStorage.setItem('layuiminiBgcolorId', bgcolorId);
miniTheme.render({
bgColorDefault: bgcolorId,
listen: false,
});
});
}
};
exports("miniTheme", miniTheme);
})
;
+248
View File
@@ -0,0 +1,248 @@
/**
* date:2020/02/27
* author:Mr.Chung
* version:2.0
* description:layuimini 主体框架扩展
*/
layui.define(["jquery","element", "miniTab"], function (exports) {
var $ = layui.$,
layer = layui.layer,
dropdown = layui.dropdown,
element = layui.element ,
miniTab = layui.miniTab;
if (!/http(s*):\/\//.test(location.href)) {
var tips = "请先将项目部署至web容器(Apache/Tomcat/Nginx/IIS/等),否则部分数据将无法显示";
return layer.alert(tips);
}
var xphp = {
/**
* 后台框架初始化
* @param options.iniUrl 后台初始化接口地址
* @param options.urlHashLocation URL地址hash定位
* @param options.bgColorDefault 默认皮肤
* @param options.multiModule 是否开启多模块
* @param options.menuChildOpen 是否展开子菜单
* @param options.loadingTime 初始化加载时间
* @param options.pageAnim iframe窗口动画
* @param options.maxTabNum 最大的tab打开数量
*/
render: function (options) {
options.iniUrl = options.iniUrl || null;
options.urlHashLocation = options.urlHashLocation || false;
options.bgColorDefault = options.bgColorDefault || 0;
options.multiModule = options.multiModule || false;
options.menuChildOpen = options.menuChildOpen || false;
options.loadingTime = options.loadingTime || 0;
options.pageAnim = options.pageAnim || false;
options.maxTabNum = options.maxTabNum || 200;
options.isHideOpenMenu = !options.isHideOpenMenu ? options.isHideOpenMenu : true;
$.getJSON(options.iniUrl, function (data) {
if (data == null) {
xphp.error('暂无菜单信息')
} else {
xphp.renderHome(data.homeInfo);
xphp.renderAnim(options.pageAnim);
xphp.listen();
dropdown.render({
elem: '.menuitemon',
data:data.menuInfo,
customName: {
id: 'id',
title: 'title',
children: 'children'
},
trigger: 'hover',
click: function(obj){
miniTab.openNewTab({...obj,href:obj.dizhi});
}
});
//tab页
miniTab.render({
filter: 'layuiminiTab',
urlHashLocation: options.urlHashLocation,
multiModule: options.multiModule,
menuChildOpen: options.menuChildOpen,
maxTabNum: options.maxTabNum,
menuList: data.menuInfo,
homeInfo: data.homeInfo,
switchtoindex:options.switchtoindex||false,
listenSwichCallback: function () {
xphp.renderDevice();
}
});
xphp.deleteLoader(options.loadingTime);
xphp.isHideOpenMenu(options.isHideOpenMenu);
}
}).fail(function () {
xphp.error('菜单接口有误');
});
},
/**
* 点击左侧下拉菜单,关闭其他下拉菜单
* @param data
*/
isHideOpenMenu: function (data) {
$(".layui-side").on("click", ".layui-nav-item", function () {
if (data) {
$(this).siblings('li').attr('class', 'layui-nav-item');
}
});
},
/**
* 初始化首页
* @param data
*/
renderHome: function (data) {
sessionStorage.setItem('layuiminiHomeHref', data.href);
$('#layuiminiHomeTabId').html('<span class="layuimini-tab-active"></span><span class="disable-close">' + data.title + '</span><i class="layui-icon layui-unselect layui-tab-close">ဆ</i>');
$('#layuiminiHomeTabId').attr('lay-id', data.id);
$('#layuiminiHomeTabIframe').html('<iframe width="100%" height="100%" frameborder="no" border="0" marginwidth="0" marginheight="0" src="' + data.href + '"></iframe>');
},
/**
* 初始化iframe窗口动画
* @param anim
*/
renderAnim: function (anim) {
if (anim) {
$('#layuimini-bg-color').after('<style id="layuimini-page-anim">' +
'.layui-tab-item.layui-show {animation:moveTop 1s;-webkit-animation:moveTop 1s;animation-fill-mode:both;-webkit-animation-fill-mode:both;position:relative;height:100%;-webkit-overflow-scrolling:touch;}\n' +
'@keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}\n' +
'@-o-keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}\n' +
'@-moz-keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}\n' +
'@-webkit-keyframes moveTop {0% {opacity:0;-webkit-transform:translateY(30px);-ms-transform:translateY(30px);transform:translateY(30px);}\n' +
' 100% {opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0);}\n' +
'}' +
'</style>');
}
},
/**
* 初始化设备端
*/
renderDevice: function () {
if (xphp.checkMobile()) {
$('.layuimini-tool i').attr('data-side-fold', 1);
$('.layuimini-tool i').attr('class', 'layui-icon layui-icon-list');
$('.layui-layout-body').removeClass('layuimini-mini');
$('.layui-layout-body').addClass('layuimini-all');
}
},
/**
* 初始化加载时间
* @param loadingTime
*/
deleteLoader: function (loadingTime) {
if (loadingTime) {
setTimeout(function () {
$('.layuimini-loader').fadeOut();
}, loadingTime * 1000)
} else {
$('.layuimini-loader').fadeOut();
}
},
/**
* 成功
* @param title
* @returns {*}
*/
success: function (title) {
return layer.msg(title, {icon: 1, shade: this.shade, scrollbar: false, time: 2000, shadeClose: true});
},
/**
* 失败
* @param title
* @returns {*}
*/
error: function (title) {
return layer.msg(title, {icon: 2, shade: this.shade, scrollbar: false, time: 3000, shadeClose: true});
},
/**
* 判断是否为手机
* @returns {boolean}
*/
checkMobile: function () {
var ua = navigator.userAgent.toLocaleLowerCase();
var pf = navigator.platform.toLocaleLowerCase();
var isAndroid = (/android/i).test(ua) || ((/iPhone|iPod|iPad/i).test(ua) && (/linux/i).test(pf))
|| (/ucweb.*linux/i.test(ua));
var isIOS = (/iPhone|iPod|iPad/i).test(ua) && !isAndroid;
var isWinPhone = (/Windows Phone|ZuneWP7/i).test(ua);
var clientWidth = document.documentElement.clientWidth;
if (!isAndroid && !isIOS && !isWinPhone && clientWidth > 1024) {
return false;
} else {
return true;
}
},
/**
* 监听
*/
listen: function () {
/**
* 监听提示信息
*/
$("body").on("mouseenter", ".layui-nav-tree .menu-li", function () {
if (xphp.checkMobile()) {
return false;
}
var classInfo = $(this).attr('class'),
tips = $(this).prop("innerHTML"),
isShow = $('.layuimini-tool i').attr('data-side-fold');
if (isShow == 0 && tips) {
tips = "<ul class='layuimini-menu-left-zoom layui-nav layui-nav-tree layui-this'><li class='layui-nav-item layui-nav-itemed'>"+tips+"</li></ul>" ;
window.openTips = layer.tips(tips, $(this), {
tips: [2, '#2f4056'],
time: 300000,
skin:"popup-tips",
success:function (el) {
var left = $(el).position().left - 10 ;
$(el).css({ left:left });
element.render();
}
});
}
});
$("body").on("mouseleave", ".popup-tips", function () {
if (xphp.checkMobile()) {
return false;
}
var isShow = $('.layuimini-tool i').attr('data-side-fold');
if (isShow == 0) {
try {
layer.close(window.openTips);
} catch (e) {
console.log(e.message);
}
}
});
/**
* 点击遮罩层
*/
$('body').on('click', '.layuimini-make', function () {
xphp.renderDevice();
});
}
};
exports("xphp", xphp);
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+101
View File
@@ -0,0 +1,101 @@
.ew-map-select-tool {
padding: 5px 15px;
box-shadow: 0 1px 0 0 rgba(0, 0, 0, .05);
}
.inline-block {
display: inline-block;
}
.layui-btn.icon-btn {
padding: 0 10px;
}
.pull-right {
float: right;
}
.map-select:after, .map-select:before {
content: '';
display: block;
clear: both;
}
.ew-map-select-poi {
height: 505px;
width: 250px;
overflow-x: hidden;
overflow-y: auto;
float: left;
position: relative;
display: block;
box-sizing: border-box;
}
.ew-map-select-search-list-item {
padding: 10px 30px 10px 15px;
border-bottom: 1px solid #e8e8e8;
cursor: pointer;
position: relative;
}
.ew-map-select-search-list-item .ew-map-select-search-list-item-title {
font-size: 14px;
color: #262626;
}
.ew-map-select-search-list-item .ew-map-select-search-list-item-address {
font-size: 12px;
color: #595959;
padding-top: 5px;
}
.ew-map-select-search-list-item-icon-ok {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
}
#ew-map-select-tips {
position: absolute;
z-index: 999;
background: #fff;
max-height: 430px;
overflow: auto;
top: 48px;
left: 56px;
width: 280px;
box-shadow: 0 2px 4px rgba(0,0,0,.12);
border: 1px solid #d2d2d2;
}
#ew-map-select-tips .ew-map-select-search-list-item {
padding: 10px 15px 10px 35px;
}
.ew-map-select-search-list-item-icon-search {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
}
.ew-map-select-search-list-item .ew-map-select-search-list-item-title {
font-size: 14px;
color: #262626;
}
.ew-map-select-search-list-item .ew-map-select-search-list-item-address {
font-size: 12px;
color: #595959;
padding-top: 5px;
}
.cur-load0 {
display: none;
background: url('./img/location.cur');
}
.cur-load1 {
display: none;
background: url('./img/location_blue.cur');
}
+759
View File
@@ -0,0 +1,759 @@
layui.define(['layer', 'locationX'], function (exports) {
var $ = layui.jquery,
layer = layui.layer,
MOD_NAME = "location",
GPS = layui.locationX;
var tpl0 = '<div class="cur-load0"></div>\n' +
'<div class="cur-load1"></div>\n' +
'<div class="ew-map-select-tool" style="position: relative;">\n' +
' 经度:<input id="lng" class="layui-input inline-block" style="width: 190px;" autocomplete="off"/>\n' +
' &nbsp;&nbsp;&nbsp;纬度:<input id="lat" class="layui-input inline-block" style="width: 190px;" autocomplete="off"/>\n' +
' <button id="ew-map-select-btn-ok" class="layui-btn icon-btn pull-right" type="button"><i\n' +
' class="layui-icon">&#xe605;</i>确定\n' +
' </button>\n' +
'</div>\n' +
'<div id="map" style="width: 100%;height: calc(100% - 48px);"></div>';
var tpl1 ='<div class="cur-load0"></div>\n' +
'<div class="cur-load1"></div>\n' +
'<div class="ew-map-select-tool" style="position: relative;">\n' +
' 搜索:<input id="ew-map-select-input-search" class="layui-input icon-search inline-block" style="width: 190px;" placeholder="输入关键字搜索" autocomplete="off" />\n' +
' <div id="ew-map-select-tips" class="ew-map-select-search-list layui-hide" style="left: 0px;width: 248px;"></div>\n' +
' &nbsp;&nbsp;经度:<input id="lng" class="layui-input inline-block" style="width: 190px;" autocomplete="off" />\n' +
' &nbsp;&nbsp;&nbsp;纬度:<input id="lat" class="layui-input inline-block" style="width: 190px;" autocomplete="off" />\n' +
' <button id="ew-map-select-btn-ok" class="layui-btn icon-btn pull-right" type="button"><i class="layui-icon">&#xe605;</i>确定</button>\n' +
'</div>\n' +
'<div class="map-select">\n' +
'\n' +
' <div id="map" style="width: 600px;height: 505px;float: right;"></div>\n' +
' <div id="ew-map-select-poi" class="layui-col-sm5 ew-map-select-search-list ew-map-select-poi">\n' +
' </div>\n' +
'\n' +
'</div>';
var obj = function (config) {
this.config = {
// 默认中心点位置是北京天安门,所有坐标系都用此坐标,偏的不大
type: 0, // 0 : 仅定位 1: 带有搜索的定位
longitude: 116.404,
latitude: 39.915,
title: '定位',
zoom: 18,
apiType: "baiduMap",
coordinate: "baiduMap",
mapType: 0,
searchKey: '村',
init: function () {
return {longitude: 116.404, latitude: 39.915};
},
success: function () {
},
onClickTip: function (data) {
console.log(data);
}
}
this.config = $.extend(this.config, config);
// 初始化经纬度信息
var initData = this.config.init();
this.config.longitude = initData.longitude;
this.config.latitude = initData.latitude;
this.lng = this.config.longitude;
this.lat = this.config.latitude;
// 转换初始坐标
this.initCoordinate = function (lng, lat) {
var o = this;
if (o.config.apiType == o.config.coordinate) {
return {lng: lng, lat: lat};
} else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'tiandiMap') {
var res = GPS.WGS84_bd(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'baiduMap') {
var res = GPS.bd_WGS84(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'baiduMap') {
var res = GPS.bd_decrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'gaodeMap') {
var res = GPS.bd_encrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'tiandiMap') {
var res = GPS.gcj_encrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'gaodeMap') {
var res = GPS.gcj_decrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
}
}
if (this.config.longitude && this.config.latitude && this.config.mapType != this.config.coordinate) {
var tbd = this.initCoordinate(this.config.longitude, this.config.latitude);
this.config.longitude = tbd.lng;
this.config.latitude = tbd.lat;
}
this.transformCoordinate = function (lng, lat) {
var o = this;
if (o.config.apiType == o.config.coordinate) {
return {lng: lng, lat: lat};
} else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'tiandiMap') {
var res = GPS.bd_WGS84(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'baiduMap') {
var res = GPS.WGS84_bd(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'baiduMap') {
var res = GPS.bd_encrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'baiduMap' && o.config.coordinate == 'gaodeMap') {
var res = GPS.bd_decrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'gaodeMap' && o.config.coordinate == 'tiandiMap') {
var res = GPS.gcj_decrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
} else if (o.config.apiType == 'tiandiMap' && o.config.coordinate == 'gaodeMap') {
var res = GPS.gcj_encrypt(lat, lng);
return {lng: res.lon.toFixed(5), lat: res.lat.toFixed(5)};
}
}
this.openBaiduMap = function () {
var o = this;
var map; // 创建地图实例
if (o.config.mapType == 1) {
map = new BMap.Map("map", {enableMapClick: false, mapType: BMAP_SATELLITE_MAP});
} else if (o.config.mapType == 2) {
map = new BMap.Map("map", {enableMapClick: false, mapType: BMAP_HYBRID_MAP});
} else {
map = new BMap.Map("map", {enableMapClick: false, mapType: BMAP_NORMAL_MAP});
}
map.enableScrollWheelZoom(); //启用滚轮放大缩小,默认禁用
var point = new BMap.Point(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915); // 创建点坐标
map.centerAndZoom(point, o.config.zoom);
map.setDefaultCursor("url('" + layui.cache.base + "location/img/location.cur') 17 35,auto"); //设置地图默认的鼠标指针样式
var marker = new BMap.Marker(map.getCenter()); // 创建标注
map.addOverlay(marker); // 将标注添加到地图中
map.addEventListener("click", function (e) {
var tbd = o.transformCoordinate(e.point.lng, e.point.lat);
//显示经纬度
$("#lng").val(tbd.lng);
$("#lat").val(tbd.lat);
o.lng = tbd.lng;
o.lat = tbd.lat;
var point = new BMap.Point(e.point.lng, e.point.lat);
map.removeOverlay(marker);
marker = new BMap.Marker(point);
map.addOverlay(marker);
if (o.config.type==1){
searchNearBy(e.point.lng, e.point.lat);
}
});
// 标记中心点
var markCenter = function (lng, lat){
var tbd = o.transformCoordinate(lng, lat);
//显示经纬度
$("#lng").val(tbd.lng);
$("#lat").val(tbd.lat);
o.lng = tbd.lng;
o.lat = tbd.lat;
var point = new BMap.Point(lng, lat);
map.removeOverlay(marker);
marker = new BMap.Marker(point);
map.addOverlay(marker);
if (o.config.type==1){
searchNearBy(lng, lat);
}
}
// 搜索附近方法
var searchNearBy = function (lng, lat){
var point = new BMap.Point(lng, lat);
var localSearch = new BMap.LocalSearch(point, {
pageCapacity: 10,
onSearchComplete: function (result){
var htmlList = '';
$.each(result,function (i,val){
$.each(val.Hr,function (i,ad){
htmlList += '<div data-lng="' + ad.point.lng + '" data-lat="' + ad.point.lat + '" data-title="'+ ad.title +'" data-address="'+ ad.address +'" class="ew-map-select-search-list-item">';
htmlList += ' <div class="ew-map-select-search-list-item-title">' + ad.title + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-address">' + ad.address + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-icon-ok layui-hide"><i class="layui-icon layui-icon-ok-circle"></i></div>';
htmlList += '</div>';
});
});
$('#ew-map-select-poi').html(htmlList);
}
});
localSearch.searchNearby([o.config.searchKey,'镇','街道','店'],point,1000);
}
// 初始化搜索
if (o.config.type==1){
o.initBaiduSearch(map,searchNearBy,markCenter);
}
}
this.initBaiduSearch = function (map,searchNearBy,markCenter){
var o = this;
searchNearBy(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915);
// poi列表点击事件
$('#ew-map-select-poi').off('click').on('click', '.ew-map-select-search-list-item', function () {
$('#ew-map-select-tips').addClass('layui-hide');
$('#ew-map-select-poi .ew-map-select-search-list-item-icon-ok').addClass('layui-hide');
$(this).find('.ew-map-select-search-list-item-icon-ok').removeClass('layui-hide');
$('#ew-map-select-center-img').removeClass('bounceInDown');
setTimeout(function () {
$('#ew-map-select-center-img').addClass('bounceInDown');
});
var lng = $(this).data('lng');
var lat = $(this).data('lat');
//
var point = new BMap.Point(lng, lat);
map.centerAndZoom(point, map.getZoom());
markCenter(lng, lat);
var title = $(this).data('title');
var address = $(this).data('address');
o.config.onClickTip({title:title,address:address,lng:lng,lat:lat});
});
// 搜索提示
var $inputSearch = $('#ew-map-select-input-search');
$inputSearch.off('input').on('input', function () {
var keywords = $(this).val();
var $selectTips = $('#ew-map-select-tips');
if (!keywords) {
$selectTips.html('');
$selectTips.addClass('layui-hide');
}
var autoComplete = new BMap.LocalSearch('全国', {
pageCapacity: 10,
onSearchComplete: function (result){
if (undefined == result){
return ;
}
var htmlList = '';
$.each(result.Hr,function (i,ad){
htmlList += '<div data-lng="' + ad.point.lng + '" data-lat="' + ad.point.lat + '" data-title="'+ ad.title +'" data-address="'+ ad.address +'" class="ew-map-select-search-list-item">';
htmlList += ' <div class="ew-map-select-search-list-item-icon-search"><i class="layui-icon layui-icon-search"></i></div>';
htmlList += ' <div class="ew-map-select-search-list-item-title">' + ad.title + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-address">' + ad.address + '</div>';
htmlList += '</div>';
});
$selectTips.html(htmlList);
if (result.Hr.length === 0) $('#ew-map-select-tips').addClass('layui-hide');
else $('#ew-map-select-tips').removeClass('layui-hide');
}
});
autoComplete.search(keywords);
});
$inputSearch.off('blur').on('blur', function () {
var keywords = $(this).val();
var $selectTips = $('#ew-map-select-tips');
if (!keywords) {
$selectTips.html('');
$selectTips.addClass('layui-hide');
}
});
$inputSearch.off('focus').on('focus', function () {
var keywords = $(this).val();
if (keywords) $('#ew-map-select-tips').removeClass('layui-hide');
});
// tips列表点击事件
$('#ew-map-select-tips').off('click').on('click', '.ew-map-select-search-list-item', function () {
$('#ew-map-select-tips').addClass('layui-hide');
var lng = $(this).data('lng');
var lat = $(this).data('lat');
var point = new BMap.Point(lng, lat);
map.centerAndZoom(point, map.getZoom());
markCenter(lng, lat);
var title = $(this).data('title');
var address = $(this).data('address');
o.config.onClickTip({title:title,address:address,lng:lng,lat:lat});
});
}
this.openTiandiMap = function () {
var o = this;
var map = new T.Map("map"); // 创建地图实例
if (o.config.mapType == 1) {
map.setMapType(TMAP_SATELLITE_MAP);
} else if (o.config.mapType == 2) {
map.setMapType(TMAP_HYBRID_MAP);
} else {
map.setMapType(TMAP_NORMAL_MAP);
}
map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放
var latLng = new T.LngLat(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915);
map.centerAndZoom(latLng, o.config.zoom);
var marker = new T.Marker(latLng); // 创建标注
map.addOverLay(marker);// 将标注添加到地图中
if (undefined === window.T.MarkTool) {
setTimeout(function () {
initMarkerTool();
}, 200);
} else {
initMarkerTool();
}
function initMarkerTool() {
var markerTool = new T.MarkTool(map, {follow: true});
markerTool.open();
/*标注事件*/
var mark = function (e) {
$.each(map.getOverlays(), function (i, marker) {
if (marker != e.currentMarker) {
map.removeOverLay(marker);
}
})
//显示经纬度
var tbd = o.transformCoordinate(e.currentLnglat.getLng(), e.currentLnglat.getLat());
$("#lng").val(tbd.lng);
$("#lat").val(tbd.lat);
o.lng = tbd.lng;
o.lat = tbd.lat;
markerTool = new T.MarkTool(map, {follow: true});
markerTool.open();
markerTool.addEventListener("mouseup", mark);
if (o.config.type==1){
searchNearBy(e.currentLnglat.getLng(), e.currentLnglat.getLat());
}
}
//绑定mouseup事件 在用户每完成一次标注时触发事件。
markerTool.addEventListener("mouseup", mark);
}
// 标记中心点
var markCenter = function (lng, lat) {
$.each(map.getOverlays(), function (i, marker) {
map.removeOverLay(marker);
})
//显示经纬度
var tbd = o.transformCoordinate(lng, lat);
$("#lng").val(tbd.lng);
$("#lat").val(tbd.lat);
o.lng = tbd.lng;
o.lat = tbd.lat;
var latLng = new T.LngLat(lng, lat);
var marker = new T.Marker(latLng); // 创建标注
map.addOverLay(marker);// 将标注添加到地图中
if (o.config.type==1){
searchNearBy(lng, lat);
}
}
// 搜索附近方法
var searchNearBy = function (lng, lat){
var point = new T.LngLat(lng,lat);
var localSearch = new T.LocalSearch(map, {
pageCapacity: 10,
onSearchComplete: function (result){
var htmlList = '';
$.each(result.getPois(),function (i,ad){
var lnglat = ad.lonlat.split(" ");
htmlList += '<div data-lng="' + lnglat[0] + '" data-lat="' + lnglat[1] + '" data-title="'+ ad.name +'" data-address="'+ ad.address +'" class="ew-map-select-search-list-item">';
htmlList += ' <div class="ew-map-select-search-list-item-title">' + ad.name + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-address">' + ad.address + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-icon-ok layui-hide"><i class="layui-icon layui-icon-ok-circle"></i></div>';
htmlList += '</div>';
});
$('#ew-map-select-poi').html(htmlList);
}
});
localSearch.setQueryType(1);
localSearch.searchNearby(o.config.searchKey,point,1000);
}
if (o.config.type==1){
o.initTiandiSearch(map,searchNearBy,markCenter);
}
}
this.initTiandiSearch = function (map,searchNearBy,markCenter){
var o = this;
searchNearBy(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915);
// poi列表点击事件
$('#ew-map-select-poi').off('click').on('click', '.ew-map-select-search-list-item', function () {
$('#ew-map-select-tips').addClass('layui-hide');
$('#ew-map-select-poi .ew-map-select-search-list-item-icon-ok').addClass('layui-hide');
$(this).find('.ew-map-select-search-list-item-icon-ok').removeClass('layui-hide');
$('#ew-map-select-center-img').removeClass('bounceInDown');
setTimeout(function () {
$('#ew-map-select-center-img').addClass('bounceInDown');
});
var lng = $(this).data('lng');
var lat = $(this).data('lat');
//
var point = new T.LngLat(lng, lat);
map.centerAndZoom(point, map.getZoom());
markCenter(lng, lat);
var title = $(this).data('title');
var address = $(this).data('address');
o.config.onClickTip({title:title,address:address,lng:lng,lat:lat});
});
// 搜索提示
var $inputSearch = $('#ew-map-select-input-search');
$inputSearch.off('input').on('input', function () {
var keywords = $(this).val();
var $selectTips = $('#ew-map-select-tips');
if (!keywords) {
$selectTips.html('');
$selectTips.addClass('layui-hide');
}
var autoComplete = new T.LocalSearch(map, {
pageCapacity: 10,
onSearchComplete: function (result){
if (undefined == result){
return ;
}
var htmlList = '';
$.each(result.getPois(),function (i,ad){
var lnglat = ad.lonlat.split(" ");
htmlList += '<div data-lng="' + lnglat[0] + '" data-lat="' + lnglat[1] + '" data-title="'+ ad.name +'" data-address="'+ ad.address +'" class="ew-map-select-search-list-item">';
htmlList += ' <div class="ew-map-select-search-list-item-title">' + ad.name + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-address">' + ad.address + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-icon-ok layui-hide"><i class="layui-icon layui-icon-ok-circle"></i></div>';
htmlList += '</div>';
});
$selectTips.html(htmlList);
if (result.getPois().length === 0) $('#ew-map-select-tips').addClass('layui-hide');
else $('#ew-map-select-tips').removeClass('layui-hide');
}
});
autoComplete.setQueryType(1);
autoComplete.search(keywords);
});
$inputSearch.off('blur').on('blur', function () {
var keywords = $(this).val();
var $selectTips = $('#ew-map-select-tips');
if (!keywords) {
$selectTips.html('');
$selectTips.addClass('layui-hide');
}
});
$inputSearch.off('focus').on('focus', function () {
var keywords = $(this).val();
if (keywords) $('#ew-map-select-tips').removeClass('layui-hide');
});
// tips列表点击事件
$('#ew-map-select-tips').off('click').on('click', '.ew-map-select-search-list-item', function () {
$('#ew-map-select-tips').addClass('layui-hide');
var lng = $(this).data('lng');
var lat = $(this).data('lat');
var point = new T.LngLat(lng, lat);
map.centerAndZoom(point, map.getZoom());
markCenter(lng, lat);
var title = $(this).data('title');
var address = $(this).data('address');
o.config.onClickTip({title:title,address:address,lng:lng,lat:lat});
});
}
this.openGaodeMap = function () {
var o = this;
// 创建地图实例
var layers = [];
if (o.config.mapType == '1') {
var satellite = new AMap.TileLayer.Satellite();
layers.push(satellite);
} else if (o.config.mapType == '2') {
var satellite = new AMap.TileLayer.Satellite();
var roadNet = new AMap.TileLayer.RoadNet();
layers.push(satellite);
layers.push(roadNet);
} else {
var layer = new AMap.TileLayer();
layers.push(layer);
}
var map = new AMap.Map("map",
{
resizeEnable: true,
zoom: o.config.zoom,
center: [o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915],
layers: layers
});
map.setDefaultCursor("url('" + layui.cache.base + "location/img/location_blue.cur') 17 35,auto");
// 初始化中间点标记
var marker = new AMap.Marker({
icon: "https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png",
position: [o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915]
});
map.add(marker);
var markCenter = function (e) {
// 标记中心点
map.clearMap();
// alert('您在[ '+e.lnglat.getLng()+','+e.lnglat.getLat()+' ]的位置点击了地图!');
//显示经纬度
var tbd = o.transformCoordinate(e.lng, e.lat);
$("#lng").val(tbd.lng);
$("#lat").val(tbd.lat);
o.lng = tbd.lng;
o.lat = tbd.lat;
var marker = new AMap.Marker({
icon: "https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png",
position: [e.lng, e.lat]
});
map.add(marker);
if (o.config.type == 1) {
searchNearBy(e.lng, e.lat);
}
}
var clickHandler = function (e) {
markCenter({lng: e.lnglat.getLng(), lat: e.lnglat.getLat()});
};
// 绑定事件
map.on('click', clickHandler);
// 附近搜索方法
var searchNearBy = function (lng, lat) {
AMap.service(['AMap.PlaceSearch'], function () {
var placeSearch = new AMap.PlaceSearch({
type: '', pageSize: 10, pageIndex: 1
});
var cpoint = [lng, lat];
placeSearch.searchNearBy('', cpoint, 1000, function (status, result) {
if (status === 'complete') {
var pois = result.poiList.pois;
var htmlList = '';
for (var i = 0; i < pois.length; i++) {
var poiItem = pois[i];
if (poiItem.location !== undefined) {
htmlList += '<div data-lng="' + poiItem.location.lng + '" data-lat="' + poiItem.location.lat + '" data-title="'+ poiItem.name +'" data-address="'+ poiItem.address +'" class="ew-map-select-search-list-item">';
htmlList += ' <div class="ew-map-select-search-list-item-title">' + poiItem.name + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-address">' + poiItem.address + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-icon-ok layui-hide"><i class="layui-icon layui-icon-ok-circle"></i></div>';
htmlList += '</div>';
}
}
$('#ew-map-select-poi').html(htmlList);
}
});
});
};
// 初始化search
if (o.config.type == 1) {
o.initGaodeSearch(map, markCenter, searchNearBy);
}
}
this.initGaodeSearch = function (map, markCenter, searchNearBy) {
var o = this;
// poi列表点击事件
$('#ew-map-select-poi').off('click').on('click', '.ew-map-select-search-list-item', function () {
$('#ew-map-select-tips').addClass('layui-hide');
$('#ew-map-select-poi .ew-map-select-search-list-item-icon-ok').addClass('layui-hide');
$(this).find('.ew-map-select-search-list-item-icon-ok').removeClass('layui-hide');
$('#ew-map-select-center-img').removeClass('bounceInDown');
setTimeout(function () {
$('#ew-map-select-center-img').addClass('bounceInDown');
});
var lng = $(this).data('lng');
var lat = $(this).data('lat');
var name = $(this).find('.ew-map-select-search-list-item-title').text();
var address = $(this).find('.ew-map-select-search-list-item-address').text();
//
map.setZoomAndCenter(map.getZoom(), [lng, lat]);
markCenter({lng: lng, lat: lat});
var title = $(this).data('title');
var address = $(this).data('address');
o.config.onClickTip({title:title,address:address,lng:lng,lat:lat});
});
searchNearBy(o.config.longitude ? o.config.longitude : 116.404, o.config.latitude ? o.config.latitude : 39.915);
// 搜索提示
var $inputSearch = $('#ew-map-select-input-search');
$inputSearch.off('input').on('input', function () {
var keywords = $(this).val();
var $selectTips = $('#ew-map-select-tips');
if (!keywords) {
$selectTips.html('');
$selectTips.addClass('layui-hide');
}
AMap.plugin('AMap.Autocomplete', function () {
var autoComplete = new AMap.Autocomplete({city: '全国'});
autoComplete.search(keywords, function (status, result) {
if (result.tips) {
var tips = result.tips;
var htmlList = '';
for (var i = 0; i < tips.length; i++) {
var tipItem = tips[i];
if (tipItem.location !== undefined) {
htmlList += '<div data-lng="' + tipItem.location.lng + '" data-lat="' + tipItem.location.lat + '" data-title="'+ tipItem.name +'" data-address="'+ tipItem.address +'" class="ew-map-select-search-list-item">';
htmlList += ' <div class="ew-map-select-search-list-item-icon-search"><i class="layui-icon layui-icon-search"></i></div>';
htmlList += ' <div class="ew-map-select-search-list-item-title">' + tipItem.name + '</div>';
htmlList += ' <div class="ew-map-select-search-list-item-address">' + tipItem.address + '</div>';
htmlList += '</div>';
}
}
$selectTips.html(htmlList);
if (tips.length === 0) $('#ew-map-select-tips').addClass('layui-hide');
else $('#ew-map-select-tips').removeClass('layui-hide');
} else {
$selectTips.html('');
$selectTips.addClass('layui-hide');
}
});
});
});
$inputSearch.off('blur').on('blur', function () {
var keywords = $(this).val();
var $selectTips = $('#ew-map-select-tips');
if (!keywords) {
$selectTips.html('');
$selectTips.addClass('layui-hide');
}
});
$inputSearch.off('focus').on('focus', function () {
var keywords = $(this).val();
if (keywords) $('#ew-map-select-tips').removeClass('layui-hide');
});
// tips列表点击事件
$('#ew-map-select-tips').off('click').on('click', '.ew-map-select-search-list-item', function () {
$('#ew-map-select-tips').addClass('layui-hide');
var lng = $(this).data('lng');
var lat = $(this).data('lat');
map.setZoomAndCenter(map.getZoom(), [lng, lat]);
markCenter({lng: lng, lat: lat});
var title = $(this).data('title');
var address = $(this).data('address');
o.config.onClickTip({title:title,address:address,lng:lng,lat:lat});
});
}
this.openMap = function () {
var o = this;
if (o.config.apiType == "baiduMap") {
var index = layer.open({
type: 1,
area: ["850px", "600px"],
title: o.config.title,
content: o.config.type == 0 ? tpl0:tpl1,
success: function () {
// 回显数据 中心标记经纬度
$("#lng").val(o.lng);
$("#lat").val(o.lat);
// 渲染地图
if (undefined === window.BMap) {
$.getScript("http://api.map.baidu.com/getscript?v=2.0&ak=tCNPmUfNmy4nTR3VYW71a6IgyWMaOSUb&services=&t=20200824135534", function () {
o.openBaiduMap();
});
} else {
o.openBaiduMap();
}
// 绑定事件
$("#ew-map-select-btn-ok").on("click", function () {
o.config.success({lng: o.lng ? o.lng : 116.404, lat: o.lat ? o.lat : 39.915});
layer.close(index);
})
}
});
} else if (o.config.apiType == "tiandiMap") {
var index = layer.open({
type: 1,
area: ["850px", "600px"],
title: o.config.title,
content: o.config.type == 0 ? tpl0:tpl1,
success: function () {
// 回显数据 中心标记经纬度
$("#lng").val(o.lng);
$("#lat").val(o.lat);
// 渲染地图
if (undefined === window.T) {
$.getScript("http://api.tianditu.gov.cn/api?v=4.0&tk=a8718394c98e9ae85b0d7af352653ce2&callback=init", function () {
o.openTiandiMap();
})
} else {
o.openTiandiMap();
}
// 绑定事件
$("#ew-map-select-btn-ok").on("click", function () {
o.config.success({lng: o.lng ? o.lng : 116.404, lat: o.lat ? o.lat : 39.915});
layer.close(index);
})
}
});
} else if (o.config.apiType == "gaodeMap") {
var index = layer.open({
type: 1,
area: ["850px", "600px"],
title: o.config.title,
content: o.config.type == 0 ? tpl0:tpl1,
success: function () {
// 回显数据 中心标记经纬度
$("#lng").val(o.lng);
$("#lat").val(o.lat);
// 渲染地图
if (undefined === window.AMap) {
$.getScript("https://webapi.amap.com/maps?v=1.4.14&key=006d995d433058322319fa797f2876f5", function () {
o.openGaodeMap();
});
} else {
o.openGaodeMap();
}
// 绑定事件
$("#ew-map-select-btn-ok").on("click", function () {
o.config.success({lng: o.lng ? o.lng : 116.404, lat: o.lat ? o.lat : 39.915});
layer.close(index);
})
}
});
}
}
};
layui.link(layui.cache.base + "location/location.css?v="+(new Date).getTime()); // 加载css
/*导出模块,用一个location对象来管理obj,不需要外部new obj*/
var location = function () {
}
location.prototype.render = function (elem, config) {
$(elem).on("click", function () {
var _this = new obj(config);
_this.openMap();
})
}
var locationObj = new location();
exports(MOD_NAME, locationObj);
})
+168
View File
@@ -0,0 +1,168 @@
layui.define(['layer'],function (exports) {
var $ = layui.jquery,
layer=layui.layer,
MOD_NAME = "locationX";
var GPS = {
PI : 3.14159265358979324,
x_pi : 3.14159265358979324 * 3000.0 / 180.0,
delta : function (lat, lon) {
// Krasovsky 1940
//
// a = 6378245.0, 1/f = 298.3
// b = a * (1 - f)
// ee = (a^2 - b^2) / a^2;
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
var dLat = this.transformLat(lon - 105.0, lat - 35.0);
var dLon = this.transformLon(lon - 105.0, lat - 35.0);
var radLat = lat / 180.0 * this.PI;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * this.PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * this.PI);
return {'lat': dLat, 'lon': dLon};
},
//WGS-84 to GCJ-02
gcj_encrypt : function (wgsLat, wgsLon) {
if (this.outOfChina(wgsLat, wgsLon))
return {'lat': wgsLat, 'lon': wgsLon};
var d = this.delta(wgsLat, wgsLon);
return {'lat' : wgsLat + d.lat,'lon' : wgsLon + d.lon};
},
//GCJ-02 to WGS-84
gcj_decrypt : function (gcjLat, gcjLon) {
if (this.outOfChina(gcjLat, gcjLon))
return {'lat': gcjLat, 'lon': gcjLon};
var d = this.delta(gcjLat, gcjLon);
return {'lat': gcjLat - d.lat, 'lon': gcjLon - d.lon};
},
//GCJ-02 to WGS-84 exactly
gcj_decrypt_exact : function (gcjLat, gcjLon) {
var initDelta = 0.01;
var threshold = 0.000000001;
var dLat = initDelta, dLon = initDelta;
var mLat = gcjLat - dLat, mLon = gcjLon - dLon;
var pLat = gcjLat + dLat, pLon = gcjLon + dLon;
var wgsLat, wgsLon, i = 0;
while (1) {
wgsLat = (mLat + pLat) / 2;
wgsLon = (mLon + pLon) / 2;
var tmp = this.gcj_encrypt(wgsLat, wgsLon)
dLat = tmp.lat - gcjLat;
dLon = tmp.lon - gcjLon;
if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold))
break;
if (dLat > 0) pLat = wgsLat; else mLat = wgsLat;
if (dLon > 0) pLon = wgsLon; else mLon = wgsLon;
if (++i > 10000) break;
}
//console.log(i);
return {'lat': wgsLat, 'lon': wgsLon};
},
//GCJ-02 to BD-09
bd_encrypt : function (gcjLat, gcjLon) {
var x = gcjLon, y = gcjLat;
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
bdLon = z * Math.cos(theta) + 0.0065;
bdLat = z * Math.sin(theta) + 0.006;
return {'lat' : bdLat,'lon' : bdLon};
},
//BD-09 to GCJ-02
bd_decrypt : function (bdLat, bdLon) {
var x = bdLon - 0.0065, y = bdLat - 0.006;
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
var gcjLon = z * Math.cos(theta);
var gcjLat = z * Math.sin(theta);
return {'lat' : gcjLat, 'lon' : gcjLon};
},
//
bd_WGS84:function(bdLat, bdLon){
var gcj=GPS.bd_decrypt(bdLat, bdLon);
return GPS.gcj_decrypt(gcj.lat,gcj.lon);
},
// 天地图坐标->百度坐标
WGS84_bd:function(bdLat, bdLon){
var gcj=GPS.gcj_encrypt(bdLat, bdLon);
return GPS.bd_encrypt(gcj.lat,gcj.lon);
},
//WGS-84 to Web mercator
//mercatorLat -> y mercatorLon -> x
mercator_encrypt : function(wgsLat, wgsLon) {
var x = wgsLon * 20037508.34 / 180.;
var y = Math.log(Math.tan((90. + wgsLat) * this.PI / 360.)) / (this.PI / 180.);
y = y * 20037508.34 / 180.;
return {'lat' : y, 'lon' : x};
/*
if ((Math.abs(wgsLon) > 180 || Math.abs(wgsLat) > 90))
return null;
var x = 6378137.0 * wgsLon * 0.017453292519943295;
var a = wgsLat * 0.017453292519943295;
var y = 3189068.5 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
return {'lat' : y, 'lon' : x};
//*/
},
// Web mercator to WGS-84
// mercatorLat -> y mercatorLon -> x
mercator_decrypt : function(mercatorLat, mercatorLon) {
var x = mercatorLon / 20037508.34 * 180.;
var y = mercatorLat / 20037508.34 * 180.;
y = 180 / this.PI * (2 * Math.atan(Math.exp(y * this.PI / 180.)) - this.PI / 2);
return {'lat' : y, 'lon' : x};
/*
if (Math.abs(mercatorLon) < 180 && Math.abs(mercatorLat) < 90)
return null;
if ((Math.abs(mercatorLon) > 20037508.3427892) || (Math.abs(mercatorLat) > 20037508.3427892))
return null;
var a = mercatorLon / 6378137.0 * 57.295779513082323;
var x = a - (Math.floor(((a + 180.0) / 360.0)) * 360.0);
var y = (1.5707963267948966 - (2.0 * Math.atan(Math.exp((-1.0 * mercatorLat) / 6378137.0)))) * 57.295779513082323;
return {'lat' : y, 'lon' : x};
//*/
},
// two point's distance
distance : function (latA, lonA, latB, lonB) {
var earthR = 6371000.;
var x = Math.cos(latA * this.PI / 180.) * Math.cos(latB * this.PI / 180.) * Math.cos((lonA - lonB) * this.PI / 180);
var y = Math.sin(latA * this.PI / 180.) * Math.sin(latB * this.PI / 180.);
var s = x + y;
if (s > 1) s = 1;
if (s < -1) s = -1;
var alpha = Math.acos(s);
var distance = alpha * earthR;
return distance;
},
outOfChina : function (lat, lon) {
if (lon < 72.004 || lon > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
},
transformLat : function (x, y) {
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0;
return ret;
},
transformLon : function (x, y) {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0;
return ret;
}
};
exports(MOD_NAME,GPS);
})
@@ -0,0 +1,96 @@
/**
消息盒子容器
*/
.lay-jsan-notice-marker {
position: absolute;
}
/**
消息盒子基础样式
*/
.lay-jsan-notice-marker-box {
/*position: absolute;*/
width: 35px;
height: 35px;
border: 1px solid #e2e2e2;
text-align: center;
line-height: 35px;
color: #e2e2e2;
float: left;
background-color: #ffffff;
}
/**
消息盒子鼠标悬停样式
*/
.lay-jsan-notice-marker-box:hover {
background-color: #f2f2f2;
color: #1E9FFF;
}
/**
消息盒子中图标样式
*/
.lay-jsan-notice-marker-icon {
font-size: 26px!important;
}
/**
最新消息,消息盒子样式
*/
.lay-jsan-notice-marker-news {
color: #FF5722!important;
}
/**
消息盒子操作按钮
*/
.lay-jsan-notice-marker-btn {
/*position: absolute;*/
width: 15px;
height: 35px;
border: 1px solid #e2e2e2;
text-align: center;
line-height: 35px;
color: #e2e2e2;
float: left;
background-color: #ffffff;
font-size: 16px;
}
.lay-jsan-notice-marker-btn:hover {
background-color: #f2f2f2;
color: #1E9FFF;
}
.lay-jsan-notice-marker-item {
padding: 5px 0 5px 10px;
background-color: #f2f2f2;
border-radius: 5px;
margin-bottom: 12px;
}
.lay-jsan-notice-marker-item:hover {
background-color: #dddddd;
cursor: pointer;
}
.lay-jsan-notice-marker-item-title-new {
color: #FF5722;
}
.lay-jsan-notice-marker-item-title {
font-size: 14px;
font-weight: bold;
}
.lay-jsan-notice-marker-item-date {
font-size: 10px;
color: #d2d2d2;
padding: 2px 0 2px 0;
}
.lay-jsan-notice-marker-item-content {
font-size: 12px;
color: #d2d2d2;
}
@@ -0,0 +1,243 @@
// noinspection ThisExpressionReferencesGlobalObjectJS
/**
* 基于layui v-2.5.4 版本,封装的消息组件
* 作者:jsan
* 日期:2019-07-28
*/
(function (root, factroy) {
typeof root.layui === "object" && layui.define ? layui.define(["layer"], function(mods){mods("jsanNotice", factroy(layui.layer))}) : factroy(root.layer);
}(this, function (layer) {
// //引入css
layui.link(layui.cache.base+"mods/extend/jsan-notice.css?v="+(new Date).getTime());
/**
* 消息盒子方法
* @param option 参数对象:
* elem 元素选择器,如:#test
* positionX 盒子左右定位位置[right,left],默认right
* positionY 盒子相对位置,可以选择不同的单位长度,如:100px
* lowKey true隐藏,false显示
* noticeWindow 详细消息窗口属性
*/
layer.noticeMarker = function (option) {
const $ = layui.$,
that = {},
POSITION_X_STYLE = typeof option["positionX"] === "undefined" || option["positionX"] === "right" ? "right: 1px;" : option["positionX"]+" 1px;",
POSITION_Y_STYPE = typeof option["positionY"] === "undefined" ? "top: 0;" : "top: "+option["positionY"]+";",
MARKER = "lay-jsan-notice-marker",
MARKER_BOX = "lay-jsan-notice-marker-box",
MARKER_BOX_NEWS = "lay-jsan-notice-marker-news",
MARKER_BOX_ICON = "layui-icon layui-icon-speaker lay-jsan-notice-marker-icon",
MARKER_BOX_BTN = "lay-jsan-notice-marker-btn",
MARKER_BOX_HIDE_BTN_ICON = "layui-icon layui-icon-"+(typeof option["positionX"] === "undefined" || option["positionX"] === "right" ? "right" : "left"),
MARKER_BOX_SHOW_BTN_ICON = "layui-icon layui-icon-"+(typeof option["positionX"] === "undefined" || option["positionX"] === "right" ? "left" : "right");
that.properties = {}; //初始化属性
$(option.elem).hide(); //隐藏初始化元素
that.properties.index = NOTICE_MARKER_INDEX++; //消息框唯一标识
that.properties.isOpen = option["lowKey"]; //初始化是否显示
that.properties.option = option; //录入初始化配置
that.properties.option.positionX = typeof option["positionX"] === "undefined" ? "right" : option["positionX"]; //初始化定位
that.properties.option.positionY = typeof option["positionY"] === "undefined" ? "right" : option["positionY"]; //初始化定位
that.properties.marker = $("<div id='notice-marker-"+that.properties.index+"' class='"+MARKER+"' style='"+POSITION_X_STYLE+POSITION_Y_STYPE+"'></div>"); //容器
that.properties.markerBoxBtn = $("<div class='"+MARKER_BOX_BTN+"'></div>"); //提示显示角标
that.properties.markerBoxBtnIcon = $("<i class='"+MARKER_BOX_HIDE_BTN_ICON+"'></i>"); //提示显示角标
that.properties.markerBox = $("<div class='"+MARKER_BOX+"'></div>"); //消息盒子
that.properties.markerBoxIcon = $("<i class='"+MARKER_BOX_ICON+"'></i>"); //消息盒子图标
//初始化默认方法
/**
* 消息提醒方法
* @param option 参数对象
* lowKey true隐藏,false显示
*/
that.remind = function(option) {
if(option["lowKey"] && that.properties.isOpen) {
//提醒时不弹出
this.hideBox();
}else if(!option["lowKey"] && !that.properties.isOpen) {
this.showBox();
}
this.properties.markerBox.addClass(MARKER_BOX_NEWS);
this.properties.markerBoxBtn.addClass(MARKER_BOX_NEWS);
};
/**
* 隐藏消息盒子方法
*/
that.hideBox = function() {
if(this.properties.isOpen) {
this.properties.markerBox.hide();
this.properties.markerBoxBtnIcon.removeClass(MARKER_BOX_HIDE_BTN_ICON);
this.properties.markerBoxBtnIcon.addClass(MARKER_BOX_SHOW_BTN_ICON);
this.properties.isOpen = false;
}
};
/**
* 显示消息盒子方法
*/
that.showBox = function() {
if(!this.properties.isOpen) {
this.properties.markerBox.show();
this.properties.markerBoxBtnIcon.removeClass(MARKER_BOX_SHOW_BTN_ICON);
this.properties.markerBoxBtnIcon.addClass(MARKER_BOX_HIDE_BTN_ICON);
this.properties.isOpen = true;
}
};
//封装渲染
that.properties.markerBoxBtn.html(that.properties.markerBoxBtnIcon);
that.properties.markerBox.html(that.properties.markerBoxIcon);
that.properties.marker.html(that.properties.option["positionX"] === "left" ? that.properties.markerBox : that.properties.markerBoxBtn);
that.properties.marker.append(that.properties.option["positionX"] === "left" ? that.properties.markerBoxBtn : that.properties.markerBox);
$("body").append(that.properties.marker);
//隐藏/显示事件
that.properties.markerBoxBtn.unbind().on("click", that, function (event) {
event.data.properties.isOpen ? event.data.hideBox() : event.data.showBox();
});
//初始化详细消息窗口
/**
* type 1:组件自带消息窗口,2:打开用户自定义窗口,默认是1
* title 消息窗口标题
* classType 消息类型(type=1时生效) 属于Object类型 {"id": "name"} 如:{"notice": "通知", "alerted": "预警", "other": "其他"}
* url 自定义消息窗口时打开的链接
* width 消息窗口宽度 可以选择不同的单位长度,如:100px
* height 消息窗口高度 可以选择不同的单位长度,如:100px
* contentWidth 消息内容窗口宽度
* contentHeight 消息内容窗口高度
*/
if(typeof that.properties.option.noticeWindow === "object" && that.properties.option.noticeWindow["type"] !== 2) {
that.noticeWindow = {};
that.noticeWindow.index = that.properties.index;
that.noticeWindow.width = typeof that.properties.option.noticeWindow["width"] === "string" ? that.properties.option.noticeWindow["width"] : "150px";
that.noticeWindow.height = typeof that.properties.option.noticeWindow["height"] === "string" ? that.properties.option.noticeWindow["height"] : "560px";
that.noticeWindow.contentWidth = typeof that.properties.option.noticeWindow["contentWidth"] === "string" ? that.properties.option.noticeWindow["contentWidth"] : "650px";
that.noticeWindow.contentHeight = typeof that.properties.option.noticeWindow["contentHeight"] === "string" ? that.properties.option.noticeWindow["contentHeight"] : "560px";
that.noticeWindow.window = $("<div id='notice-marker-window-"+that.noticeWindow.index+"' class='layui-tab' lay-filter='notice-marker-window-"+that.noticeWindow.index+"'></div>");
that.noticeWindow.tabTitle = $("<ul id='notice-marker-window-title-"+that.noticeWindow.index+"' class='layui-tab-title'></ul>");
that.noticeWindow.tabContent = $("<div id='notice-marker-window-content-"+that.noticeWindow.index+"' class='layui-tab-content'></div>");
const classType = {};
if(typeof that.properties.option.noticeWindow["classType"] === "object") {
for(let tab in that.properties.option.noticeWindow["classType"]) {
classType[tab] = ["<li id='notice-marker-window-title-"+tab+"'>"+that.properties.option.noticeWindow["classType"][tab]+"<span class='layui-badge-dot layui-hide'></span></li>", "<div id='notice-marker-window-content-"+tab+"' lay-id='notice-marker-window-content-"+tab+"' class='layui-tab-item'></div>"];
}
}else {
classType['notice'] = ["<li id='notice-marker-window-title-notice'>消息</li>", "<div id='notice-marker-window-content-notice' lay-id='notice-marker-window-content-\"+tab+\"' class='layui-tab-item'></div>"];
}
that.noticeWindow.classType = classType;
//详细消息窗口渲染
that.noticeWindow.window.html(that.noticeWindow.tabTitle);
that.noticeWindow.window.append(that.noticeWindow.tabContent);
for(let tab in that.noticeWindow.classType) {
that.noticeWindow.tabTitle.append(that.noticeWindow.classType[tab][0]);
that.noticeWindow.tabContent.append(that.noticeWindow.classType[tab][1]);
}
that.noticeWindow.window.hide();
that.noticeWindow.tabTitle.find("li").eq(0).addClass("layui-this");
that.noticeWindow.tabContent.find(".layui-tab-item").eq(0).addClass("layui-show");
$("body").append(that.noticeWindow.window);
layui.use('element', function(){});
that.properties.markerBox.unbind().on("click", that, function (event) {
event.data.hideBox();
layer.open({
type: 1,
id: "notice-marker-window-layer-"+that.noticeWindow.index,
title: typeof event.data.properties.option.noticeWindow["title"] === "string" ? event.data.properties.option.noticeWindow["title"] : "<i class='layui-icon layui-icon-friends'></i>",
area: [event.data.noticeWindow.width - 12, event.data.noticeWindow.height],
offset: [event.data.properties.option.positionY, (that.properties.option["positionX"] === "right" ? ($("body").width()-16-Number(that.noticeWindow.width.replace("px", "")))+"px" : "16px")],
shade: 0,
maxmin: true,
content: $("#"+event.data.noticeWindow.window.attr("id")),
cancel: function () {
that.noticeWindow.window.hide();
}
});
event.data.properties.markerBox.removeClass(MARKER_BOX_NEWS);
event.data.properties.markerBoxBtn.removeClass(MARKER_BOX_NEWS);
});
/**
* 向消息窗口推送消息
* @param option 参数对象
* lowKey 是否使用盒子提醒 true不提醒,false提醒
* classTypeId 消息所属消息类型的id
* content 需要推送的类型集合 Array,每组数据包括:
* title 消息标题。最大27位长度,大于27会自动省略
* content 消息内容。最大44位长度,大于44位自动省略
* date 消息发布时间 yyyy-MM-dd HH:mm:ss
* url 点击消息后跳转位置
*/
that.addNews = function (option) {
for(let i in option.content) {
const item = $("<div class='lay-jsan-notice-marker-item' notice-url = '" + option.content[i]["url"] + "'></div>");
item.append("<div class='lay-jsan-notice-marker-item-title lay-jsan-notice-marker-item-title-new'>"+(option.content[i]["title"].length > 28 ? option.content[i]["title"].substring(0, 25)+"..." : option.content[i]["title"] )+"</div>");
item.append("<div class='lay-jsan-notice-marker-item-date'>"+option.content[i]["date"]+"</div>");
item.append("<div class='lay-jsan-notice-marker-item-content'>"+(option.content[i]["content"].length > 45 ? option.content[i]["content"].substring(0, 43)+"..." : option.content[i]["content"])+"</div>");
$("#notice-marker-window-content-"+option["classTypeId"]).prepend(item);
}
$("#notice-marker-window-title-"+option["classTypeId"]).find(".layui-badge-dot").removeClass("layui-hide");
noticeMarkerItemEvent(option, this);
const lowKey = typeof option["lowKey"] === "undefined" ? false : option["lowKey"];
this.remind({"lowKey": lowKey});
};
var noticeMarkerItemEvent = function (option, that) {
$("#notice-marker-window-content-"+option["classTypeId"]+" .lay-jsan-notice-marker-item").unbind().on("click", function (event) {
$(this).find(".lay-jsan-notice-marker-item-title").eq(0).removeClass("lay-jsan-notice-marker-item-title-new");
if($("#notice-marker-window-content-"+option["classTypeId"]).find(".lay-jsan-notice-marker-item-title-new").length === 0) {
$("#notice-marker-window-title-"+option["classTypeId"]).find(".layui-badge-dot").addClass("layui-hide");
}
$(this).attr("notice-url");
layer.open({
type: 2,
title: that.properties.option.noticeWindow["title"],
area: [that.noticeWindow.contentWidth, that.noticeWindow.contentHeight],
shade: 0,
maxmin: true,
content: $(this).attr("notice-url")
});
});
}
}else if(typeof that.properties.option.noticeWindow === "object" && that.properties.option.noticeWindow["type"] === 2) {
that.noticeWindow = {};
that.noticeWindow.index = that.properties.index;
that.noticeWindow.url = that.properties.option.noticeWindow["url"];
that.noticeWindow.width = typeof that.properties.option.noticeWindow["width"] === "string" ? that.properties.option.noticeWindow["width"] : "150px";
that.noticeWindow.height = typeof that.properties.option.noticeWindow["height"] === "string" ? that.properties.option.noticeWindow["height"] : "560px";
that.properties.markerBox.unbind().on("click", that, function (event) {
event.data.hideBox();
layer.open({
type: 2,
id: "notice-marker-window-layer-"+that.noticeWindow.index,
title: typeof event.data.properties.option.noticeWindow["title"] === "string" ? event.data.properties.option.noticeWindow["title"] : "<i class='layui-icon layui-icon-friends'></i>",
area: [event.data.noticeWindow.width, event.data.noticeWindow.height],
offset: [event.data.properties.option.positionY, (that.properties.option["positionX"] === "right" ? ($("body").width()-16-Number(that.noticeWindow.width.replace("px", "")))+"px" : "16px")],
shade: 0,
maxmin: true,
content: event.data.noticeWindow.url
});
});
}
//初始显示设置
that.properties.isOpen ? that.hideBox() : that.showBox();
return that;
};
return {mod: "jsanNotice", v: "1.0.11"};
}));
NOTICE_MARKER_INDEX = 1;
+78
View File
@@ -0,0 +1,78 @@
(function(root,factroy){
typeof root.layui === 'object' && layui.define ? layui.define(function(mods){mods('mods',factroy(layui))}) : null;
}(this,function(layui){
'use strict';
// 预定义插件列表
var list = {
jsanNotice:'mods/extend/jsan-notice'
};
// 插件加载器
var mods = function(mod_name,callback){
var extend = {};
// 如果是官方模块
// 引入单个插件
if(typeof mod_name === 'string'){
if(!isLayui(mod_name)){
if(typeof list[mod_name] !== 'string') throw new Error('引入的插件'+mod_name+'不在预定义列表中');
extend[mod_name] = list[mod_name];
}
}
// 批量引入插件
else if(Array.isArray(mod_name)){
for(var i=0,item;item = mod_name[i++];){
if(!isLayui(item)){
if(typeof list[item] !== 'string') throw new Error('引入的插件'+item+'不在预定义列表中');
extend[item] = list[item];
}
}
}
else{
throw new Error('mods()中,传入了无效的参数');
}
if(typeof callback !== 'function') throw Error('第二个参数必须是函数');
layui.extend(extend).use(mod_name,function(){
var arg = [];
for(var i=0,item;item = arguments[i++];){
arg.push(item);
}
callback.apply(layui,arg);
});
}
var isLayui = function(mod){
return typeof layui_mods[mod] === 'string' ? true : false;
};
if(typeof Array.isArray !== 'function'){
Array.isArray = function(val){
return Object.prototype.toString.call(val) === '[object Array]' ? true : false;
}
}
var layui_mods = {
layer:'layer',
laydate:'laydate',
layedit:'layedit',
laypage:'laypage',
laytpl:'laytpl',
table:'table',
form:'form',
upload:'upload',
jquery:'jquery',
code:'code',
carousel:'carousel',
element:'element',
flow:'flow',
mobile:'mobile',
rate:'rate',
tree:'tree',
util:'util',
};
return mods;
}));
+101
View File
@@ -0,0 +1,101 @@
/**
* @Name: 基于layui
* @Author: 潘晨晨
* 最近修改时间: 2021/04/22
*/
layui.define(['jquery'],function(exports){
var $ = layui.jquery;
var obj ={
init:function(element){
//默认
element.data = element.data || [ {name:'暂无数据'}]
element.rows = element.rows || 1
element.moreUpText = element.moreUpText || '更多'
element.moreDownText = element.moreDownText || '收起'
element.href = element.href || false
element.herfBlank = element.herfBlank?'_blank':'_self'
element.themeColor = element.themeColor || 'green'
element.size = element.size ? 'layui-btn-'+element.size :'layui-btn'
var html;
var box = $(element.elemId);
var boxHeight ;
var itemHeight ;
var flag = true; // 如果外面包裹了pack的变false
var dyc = false;
box.addClass('box-item-pcc');
element.data.forEach(e=>{
boxHeight = box.height();
itemHeight = $('.item-pcc').outerHeight(true);
element.href ? html = '<a class="item-pcc layui-bg-'+element.themeColor+' '+element.size+'" href="'+element.href+'?id='+e.id+'" target="'+element.herfBlank+'">'+e.name+'</a>':html = '<span class="item-pcc layui-bg-'+element.themeColor+' '+element.size+'">'+e.name+'</span>'
if((boxHeight>itemHeight*element.rows) && flag){
morePosition();
flag =false;
}
box.append(html);
})
boxHeight = box.height();
itemHeight = $('.item-pcc').outerHeight(true);
if(boxHeight>itemHeight*element.rows){
more(); //第一次渲染页面
dyc =true
}
$(document).on('click','#more-pcc',function(){ //点击更多
more()
})
function morePosition(el){
if(flag){
if(el&&dyc){ //如果不是第一次渲染并且
box.find('#more-box-pcc').remove();
}
else{
$('.item-pcc').wrapAll("<span class='pack-pcc'></span>")
}
var list = $('.pack-pcc .item-pcc').eq(-3);
list.before('<span id="more-box-pcc" class="item-pcc more-pcc '+element.size+'"><a class="layui-font-'+element.themeColor+' " id="more-pcc">'+element.moreUpText+'</a></span>');
}
}
function more(){
if(box.height()>itemHeight*element.rows){
box.css('height',itemHeight*element.rows);
morePosition(true)
}else{
box.css('height','auto');
box.find('#more-box-pcc').remove();
box.append('<span id="more-box-pcc" class="item-pcc more-pcc '+element.size+'"><a class="layui-font-'+element.themeColor+'" id="more-pcc">'+element.moreDownText+'</a></span>');
flag = true;
}
}
}
}
var $style = $('<style type="text/css">\
.box-item-pcc { overflow: hidden; display: inline-block; width: 70%;} \
.box-item-pcc .item-pcc { margin:0 5px 5px 5px; box-sizing: border-box; display: inline-block; }\
.box-item-pcc .more-pcc { float: right; background: none; }\
.box-item-pcc a { cursor: pointer; }\
.layui-font-red{color:#FF5722!important}\
.layui-font-orange{color:#FFB800!important}\
.layui-font-green{color:#009688!important}\
.layui-font-cyan{color:#2F4056!important}\
.layui-font-blue{color:#01AAED!important}\
.layui-font-black{color:#000!important}\
.layui-font-gray{color:#c2c2c2!important}\
</style>');
$($('head')[0]).append($style);
exports('moreMenus',obj);
});
+228
View File
@@ -0,0 +1,228 @@
.toast-title {
font-weight: bold;
}
.toast-message {
-ms-word-wrap: break-word;
word-wrap: break-word;
}
.toast-message a,
.toast-message label {
color: #FFFFFF;
}
.toast-message a:hover {
color: #CCCCCC;
text-decoration: none;
}
.toast-close-button {
position: relative;
right: -0.3em;
top: -0.3em;
float: right;
font-size: 20px;
font-weight: bold;
color: #FFFFFF;
-webkit-text-shadow: 0 1px 0 #ffffff;
text-shadow: 0 1px 0 #ffffff;
opacity: 0.8;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
filter: alpha(opacity=80);
line-height: 1;
}
.toast-close-button:hover,
.toast-close-button:focus {
color: #000000;
text-decoration: none;
cursor: pointer;
opacity: 0.4;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
filter: alpha(opacity=40);
}
.rtl .toast-close-button {
left: -0.3em;
float: left;
right: 0.3em;
}
/*Additional properties for button version
iOS requires the button element instead of an anchor tag.
If you want the anchor version, it requires `href="#"`.*/
button.toast-close-button {
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
}
.toast-top-center {
top: 0;
right: 0;
width: 100%;
}
.toast-bottom-center {
bottom: 0;
right: 0;
width: 100%;
}
.toast-top-full-width {
top: 0;
right: 0;
width: 100%;
}
.toast-bottom-full-width {
bottom: 0;
right: 0;
width: 100%;
}
.toast-top-left {
top: 12px;
left: 12px;
}
.toast-top-right {
top: 12px;
right: 12px;
}
.toast-bottom-right {
right: 12px;
bottom: 12px;
}
.toast-bottom-left {
bottom: 12px;
left: 12px;
}
#toast-container {
position: fixed;
z-index: 999999;
pointer-events: none;
/*overrides*/
}
#toast-container * {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
#toast-container > div {
position: relative;
pointer-events: auto;
overflow: hidden;
margin: 0 0 6px;
padding: 15px 15px 15px 50px;
width: 300px;
-moz-border-radius: 3px 3px 3px 3px;
-webkit-border-radius: 3px 3px 3px 3px;
border-radius: 3px 3px 3px 3px;
background-position: 15px center;
background-repeat: no-repeat;
-moz-box-shadow: 0 0 12px #999999;
-webkit-box-shadow: 0 0 12px #999999;
box-shadow: 0 0 12px #999999;
color: #FFFFFF;
opacity: 0.8;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
filter: alpha(opacity=80);
}
#toast-container > div.rtl {
direction: rtl;
padding: 15px 50px 15px 15px;
background-position: right 15px center;
}
#toast-container > div:hover {
-moz-box-shadow: 0 0 12px #000000;
-webkit-box-shadow: 0 0 12px #000000;
box-shadow: 0 0 12px #000000;
opacity: 1;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
filter: alpha(opacity=100);
cursor: pointer;
}
#toast-container > .toast-info {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important;
}
#toast-container > .toast-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important;
}
#toast-container > .toast-success {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important;
}
#toast-container > .toast-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important;
}
#toast-container.toast-top-center > div,
#toast-container.toast-bottom-center > div {
width: 300px;
margin-left: auto;
margin-right: auto;
}
#toast-container.toast-top-full-width > div,
#toast-container.toast-bottom-full-width > div {
width: 96%;
margin-left: auto;
margin-right: auto;
}
.toast {
background-color: #030303;
}
.toast-success {
background-color: #51A351;
}
.toast-error {
background-color: #BD362F;
}
.toast-info {
background-color: #2F96B4;
}
.toast-warning {
background-color: #F89406;
}
.toast-progress {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
background-color: #000000;
opacity: 0.4;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
filter: alpha(opacity=40);
}
/*Responsive Design*/
@media all and (max-width: 240px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 11em;
}
#toast-container > div.rtl {
padding: 8px 50px 8px 8px;
}
#toast-container .toast-close-button {
right: -0.2em;
top: -0.2em;
}
#toast-container .rtl .toast-close-button {
left: -0.2em;
right: 0.2em;
}
}
@media all and (min-width: 241px) and (max-width: 480px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 18em;
}
#toast-container > div.rtl {
padding: 8px 50px 8px 8px;
}
#toast-container .toast-close-button {
right: -0.2em;
top: -0.2em;
}
#toast-container .rtl .toast-close-button {
left: -0.2em;
right: 0.2em;
}
}
@media all and (min-width: 481px) and (max-width: 768px) {
#toast-container > div {
padding: 15px 15px 15px 50px;
width: 25em;
}
#toast-container > div.rtl {
padding: 15px 50px 15px 15px;
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More