家谱现有接口调试50%
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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); }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* 注册页只保留单页差异,公共认证卡片样式在 public.css */
|
||||
.page-register .auth-links {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.page-register .auth-layout {
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 628 KiB |
|
After Width: | Height: | Size: 784 KiB |
|
After Width: | Height: | Size: 428 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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.json,albumName 必填。
|
||||
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.json,ossId 必填。
|
||||
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
|
||||
};
|
||||
});
|
||||
@@ -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()
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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.json,ceremonyType 和 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.json,donorName 和 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
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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.json,recordTitle 必填。
|
||||
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
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
});
|
||||
|
After Width: | Height: | Size: 371 B |
|
After Width: | Height: | Size: 43 B |
|
After Width: | Height: | Size: 1.4 KiB |
@@ -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;
|
||||
}
|
||||
|
After Width: | Height: | Size: 22 KiB |
@@ -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
|
||||
});
|
||||
@@ -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}
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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 */
|
||||
@@ -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="删除标签">×</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);
|
||||
}
|
||||
}));
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
@@ -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、分组展示按照:groupName,groupChildren 数组 [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);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
})
|
||||
;
|
||||
@@ -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);
|
||||
});
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@@ -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');
|
||||
}
|
||||
@@ -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' +
|
||||
' 纬度:<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"></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' +
|
||||
' 经度:<input id="lng" class="layui-input inline-block" style="width: 190px;" autocomplete="off" />\n' +
|
||||
' 纬度:<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"></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);
|
||||
})
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}));
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||