parent
6f68c79f18
commit
eabad1165a
@ -0,0 +1,22 @@
|
||||
class Ecs::UsersController < Ecs::BaseController
|
||||
skip_before_action :check_user_permission!
|
||||
before_action :check_manager_permission!
|
||||
|
||||
def index
|
||||
users = UserQuery.call(params)
|
||||
|
||||
@count = users.count
|
||||
@users = paginate users.includes(user_extension: [:school, :department])
|
||||
@manager_ids = current_major_school.ec_major_school_users.pluck(:user_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_major_school
|
||||
@_ec_major_school ||= EcMajorSchool.find(params[:ec_major_school_id])
|
||||
end
|
||||
|
||||
def current_school
|
||||
@_current_school ||= current_major_school.school
|
||||
end
|
||||
end
|
@ -1,3 +1,9 @@
|
||||
class ApplicationQuery
|
||||
include Callable
|
||||
|
||||
private
|
||||
|
||||
def strip_param(key)
|
||||
params[key].to_s.strip.presence
|
||||
end
|
||||
end
|
@ -0,0 +1,28 @@
|
||||
class UserQuery < ApplicationQuery
|
||||
attr_reader :params
|
||||
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def call
|
||||
users = User.where(type: 'User')
|
||||
|
||||
# 真实姓名
|
||||
if name = strip_param(:name)
|
||||
users = users.where('LOWER(CONCAT(users.lastname, users.firstname)) LIKE ?', "%#{name.downcase}%")
|
||||
end
|
||||
|
||||
# 单位名称
|
||||
if school = strip_param(:school)
|
||||
users = users.joins(user_extension: :school).where('schools.name LIKE ?', "%#{school}%")
|
||||
end
|
||||
|
||||
# 职业
|
||||
if (identity = strip_param(:identity)) && UserExtension.identities.keys.include?(identity)
|
||||
users = users.joins(:user_extension).where(user_extensions: { identity: identity })
|
||||
end
|
||||
|
||||
users
|
||||
end
|
||||
end
|
@ -1,2 +1,7 @@
|
||||
json.count @count
|
||||
json.es_majors @ec_majors, partial: 'ecs/majors/shared/ec_major', as: :ec_major
|
||||
json.ec_majors do
|
||||
json.array! @ec_majors.each do |major|
|
||||
json.extract! major, :id, :name, :code
|
||||
json.selected @major_ids.include?(major.id)
|
||||
end
|
||||
end
|
||||
|
@ -1 +0,0 @@
|
||||
json.partial! 'ecs/shared/user', user: @user
|
@ -0,0 +1,12 @@
|
||||
json.count @count
|
||||
json.users do
|
||||
json.array! @users.each do |user|
|
||||
json.id user.id
|
||||
json.name user.real_name
|
||||
json.identity user.identity
|
||||
json.school_name user.school_name
|
||||
json.department_name user.department_name
|
||||
json.phone Util.conceal(user.phone, :phone)
|
||||
json.manager @manager_ids.include?(user.id)
|
||||
end
|
||||
end
|
After Width: | Height: | Size: 79 KiB |
@ -0,0 +1,173 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Modal, Input, Table, message } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
import './AddMajorModal.scss';
|
||||
|
||||
const { Search } = Input;
|
||||
const tableColumns = [
|
||||
{ title: '专业代码', dataIndex: 'code', key: 'code', width: 100, },
|
||||
{ title: '专业名称', dataIndex: 'name', key: 'name', },
|
||||
{ title: '', dataIndex: 'selected', key: 'selected', width: 80, render: selected => selected && <span className="color-orange">已选择</span> },
|
||||
];
|
||||
const defaultPagination = { current: 1, pageSize: 10, total: 0 };
|
||||
|
||||
class AddMajorModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
confirmLoading: false,
|
||||
error: '',
|
||||
keyword: '',
|
||||
pagination: {...defaultPagination},
|
||||
|
||||
schoolId: props.schoolId,
|
||||
majorData: [],
|
||||
selectedData: []
|
||||
}
|
||||
|
||||
this.getMajors = this.getMajors.bind(this);
|
||||
this.selectMajor = this.selectMajor.bind(this);
|
||||
this.onAfterModalClose = this.onAfterModalClose.bind(this);
|
||||
this.handleOk = this.handleOk.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.onPaginationChange = this.onPaginationChange.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if(!prevProps.visible && this.props.visible){
|
||||
this.getMajors();
|
||||
}
|
||||
}
|
||||
|
||||
getMajors(){
|
||||
let { schoolId, keyword, pagination } = this.state;
|
||||
|
||||
this.setState({ loading: true });
|
||||
axios.get(`/schools/${schoolId}/ec_majors.json`, {
|
||||
params: {
|
||||
search: keyword,
|
||||
page: pagination.current,
|
||||
per_page: pagination.pageSize
|
||||
}
|
||||
}).then(res => {
|
||||
if(res.status === 200){
|
||||
const pagination = { ...this.state.pagination };
|
||||
pagination.total = res.data.count;
|
||||
|
||||
this.setState({
|
||||
majorData: res.data.ec_majors,
|
||||
loading: false,
|
||||
pagination,
|
||||
})
|
||||
}
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
this.setState({ loading: false })
|
||||
})
|
||||
}
|
||||
|
||||
getCheckboxProps(record){
|
||||
return { ...record, disabled: record.selected }
|
||||
}
|
||||
|
||||
selectMajor(selectedRowKeys){
|
||||
this.setState({ selectedData: selectedRowKeys });
|
||||
}
|
||||
|
||||
onPaginationChange(page, pageSize){
|
||||
this.setState({ pagination: { current: page, pageSize: pageSize } }, () => {
|
||||
this.getMajors()
|
||||
});
|
||||
}
|
||||
|
||||
handleOk(){
|
||||
let { selectedData } = this.state;
|
||||
|
||||
if(selectedData.length === 0){
|
||||
this.setState({ error: '请选择专业' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitMajor(selectedData);
|
||||
}
|
||||
|
||||
handleCancel(){
|
||||
this.props.onHide(false);
|
||||
}
|
||||
|
||||
onAfterModalClose(){
|
||||
this.setState({
|
||||
error: '',
|
||||
keyword: '',
|
||||
pagination: {...defaultPagination},
|
||||
majorData: [],
|
||||
selectedData: [],
|
||||
});
|
||||
}
|
||||
|
||||
submitMajor(ids) {
|
||||
let { schoolId } = this.state;
|
||||
|
||||
this.setState({ confirmLoading: true });
|
||||
axios.post(`/schools/${schoolId}/ec_major_schools.json`, { major_ids: ids }).then(res => {
|
||||
if(res.status === 200){
|
||||
message.success('操作成功');
|
||||
this.setState({ confirmLoading: false });
|
||||
this.props.onHide(true);
|
||||
}
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
this.setState({ confirmLoading: false });
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
let { loading, keyword, majorData, selectedData, pagination } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
title="添加认证专业"
|
||||
wrapClassName="add-major-modal"
|
||||
visible={this.props.visible}
|
||||
confirmLoading={this.state.confirmLoading}
|
||||
afterClose={this.onAfterModalClose}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}>
|
||||
|
||||
<div className="add-major-search">
|
||||
<Search
|
||||
placeholder="专业代码/专业名称检索"
|
||||
onInput={e => this.setState({keyword: e.target.value})}
|
||||
onSearch={this.getMajors}
|
||||
value={keyword}/>
|
||||
</div>
|
||||
|
||||
<div className="add-major-body">
|
||||
<Table rowKey="id"
|
||||
rowSelection={{onChange: this.selectMajor, getCheckboxProps: this.getCheckboxProps, selectedRowKeys: selectedData}}
|
||||
loading={loading}
|
||||
columns={tableColumns}
|
||||
dataSource={majorData}
|
||||
pagination={{...pagination, onChange: this.onPaginationChange}}
|
||||
size="small"
|
||||
scroll={{ y: 200 }}/>
|
||||
<div className="error">{ this.state.error }</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AddMajorModal.propTypes = {
|
||||
schoolId: PropTypes.number,
|
||||
visible: PropTypes.bool,
|
||||
onHide: PropTypes.func
|
||||
}
|
||||
|
||||
export default AddMajorModal
|
@ -0,0 +1,28 @@
|
||||
.add-major-modal {
|
||||
.add-major-search {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.ant-modal-body {
|
||||
padding-bottom: 0;
|
||||
|
||||
.major-row {
|
||||
padding: 10px;
|
||||
}
|
||||
.ant-table-thead {
|
||||
background: #fafafa;
|
||||
}
|
||||
.ant-table-scroll {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.error {
|
||||
height: 20px;
|
||||
margin-top: -20px;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
.ant-modal-footer {
|
||||
text-align: center;
|
||||
border-top: unset;
|
||||
}
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Modal, Input, Table, message, Select, Form, Row, Col, Button } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
import './AddManagerModal.scss';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const columnRender = (text) => <div style={{ wordWrap: 'break-word', wordBreak: 'break-all' }}> {text} </div>
|
||||
const tableColumns = [
|
||||
{ title: '姓名', dataIndex: 'name', key: 'name', width: 60, render: columnRender },
|
||||
{ title: '职称', dataIndex: 'identity', key: 'identity', width: 60, },
|
||||
{ title: '单位', dataIndex: 'school_name', key: 'school_name', render: (_, record) => columnRender(`${record.school_name} ${record.department_name}`) },
|
||||
{ title: '手机号', dataIndex: 'phone', key: 'phone', width: 80, },
|
||||
];
|
||||
const defaultPagination = { current: 1, pageSize: 20, total: 0 };
|
||||
|
||||
class AddManagerModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
confirmLoading: false,
|
||||
nameValidateStatus: '',
|
||||
error: '',
|
||||
name: '',
|
||||
school: props.schoolName,
|
||||
identity: '',
|
||||
pagination: {...defaultPagination},
|
||||
|
||||
schoolId: props.schoolId,
|
||||
userData: [],
|
||||
selectedData: []
|
||||
}
|
||||
|
||||
this.getUsers = this.getUsers.bind(this);
|
||||
this.selectUser = this.selectUser.bind(this);
|
||||
this.onAfterModalClose = this.onAfterModalClose.bind(this);
|
||||
this.handleOk = this.handleOk.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.onPaginationChange = this.onPaginationChange.bind(this);
|
||||
}
|
||||
|
||||
getUsers(){
|
||||
let { majorId } = this.props;
|
||||
let { name, school, identity, pagination } = this.state;
|
||||
|
||||
if(name.length === 0){
|
||||
this.setState({ nameValidateStatus: 'error' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
axios.get(`/ec_major_schools/${majorId}/users.json`, {
|
||||
params: {
|
||||
name, school, identity,
|
||||
page: pagination.current,
|
||||
per_page: pagination.pageSize
|
||||
}
|
||||
}).then(res => {
|
||||
if(res.status === 200){
|
||||
const pagination = { ...this.state.pagination };
|
||||
pagination.total = res.data.count;
|
||||
|
||||
this.setState({
|
||||
userData: res.data.users,
|
||||
loading: false,
|
||||
pagination,
|
||||
})
|
||||
}
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
this.setState({ loading: false })
|
||||
})
|
||||
}
|
||||
|
||||
getCheckboxProps(record){
|
||||
return { ...record, disabled: record.manager }
|
||||
}
|
||||
|
||||
selectUser(selectedRowKeys){
|
||||
this.setState({ selectedData: selectedRowKeys });
|
||||
}
|
||||
|
||||
onPaginationChange(page, pageSize){
|
||||
this.setState({ pagination: { current: page, pageSize: pageSize } }, () => {
|
||||
this.getUsers()
|
||||
});
|
||||
}
|
||||
|
||||
onNameChange = (e) => {
|
||||
let name = e.target.value;
|
||||
let nameValidateStatus = '';
|
||||
|
||||
if(name.length === 0){
|
||||
nameValidateStatus = 'error'
|
||||
}
|
||||
|
||||
this.setState({ nameValidateStatus, name });
|
||||
}
|
||||
|
||||
handleOk(){
|
||||
this.setState({ error: '' });
|
||||
let { selectedData } = this.state;
|
||||
|
||||
if(selectedData.length === 0){
|
||||
this.setState({ error: '请选择至少一个用户' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.submitUsers(selectedData);
|
||||
}
|
||||
|
||||
handleCancel(){
|
||||
this.props.onHide(false);
|
||||
}
|
||||
|
||||
onAfterModalClose(){
|
||||
this.setState({
|
||||
error: '',
|
||||
nameValidateStatus: '',
|
||||
name: '',
|
||||
school: this.props.schoolName,
|
||||
identity: '',
|
||||
pagination: {...defaultPagination},
|
||||
userData: [],
|
||||
selectedData: [],
|
||||
});
|
||||
}
|
||||
|
||||
submitUsers(ids) {
|
||||
let { majorId } = this.props;
|
||||
|
||||
this.setState({ confirmLoading: true });
|
||||
axios.post(`/ec_major_schools/${majorId}/major_managers.json`, { user_ids: ids }).then(res => {
|
||||
if(res.status !== 200){ return }
|
||||
|
||||
message.success('操作成功');
|
||||
this.setState({ confirmLoading: false });
|
||||
this.props.onHide(true);
|
||||
}).catch(e => {
|
||||
console.log(e);
|
||||
this.setState({ confirmLoading: false });
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
let { loading, name, school, identity, userData, selectedData, pagination, nameValidateStatus } = this.state;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
title="添加管理员"
|
||||
wrapClassName="add-ec-manager-modal"
|
||||
visible={this.props.visible}
|
||||
confirmLoading={this.state.confirmLoading}
|
||||
afterClose={this.onAfterModalClose}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}>
|
||||
|
||||
<div className="add-ec-manager-search">
|
||||
<Form layout="horizontal">
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Form.Item label="姓名" labelCol={{ span: 6 }} wrapperCol={{span: 16}} validateStatus={nameValidateStatus}>
|
||||
<Input onChange={this.onNameChange} value={name} placeholder="请输入姓名" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="职业" labelCol={{ span: 6 }} wrapperCol={{span: 17}} >
|
||||
<Select value={identity} onChange={value => this.setState({ identity: value }) } placeholder="请选择职业">
|
||||
<Option value="">全部</Option>
|
||||
<Option value="teacher">教师</Option>
|
||||
<Option value="student">学生</Option>
|
||||
<Option value="professional">专业人士</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={18}>
|
||||
<Form.Item label="学校" labelCol={{ span: 4 }} wrapperCol={{span: 18}}>
|
||||
<Input onChange={e => this.setState({ school: e.target.value })} value={school} placeholder="请输入学校名称"/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4} offset={2}>
|
||||
<Button type="primary" className="mt5" onClick={this.getUsers}>搜索</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div className="add-ec-manager-body">
|
||||
<Table rowKey="id"
|
||||
rowSelection={{onChange: this.selectUser, getCheckboxProps: this.getCheckboxProps, selectedRowKeys: selectedData, columnWidth: 40}}
|
||||
loading={loading}
|
||||
columns={tableColumns}
|
||||
dataSource={userData}
|
||||
pagination={{...pagination, onChange: this.onPaginationChange}}
|
||||
size="small"
|
||||
scroll={{ y: 200 }}/>
|
||||
<div className="error">{ this.state.error }</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AddManagerModal.propTypes = {
|
||||
schoolId: PropTypes.string,
|
||||
schoolName: PropTypes.string,
|
||||
majorId: PropTypes.number,
|
||||
visible: PropTypes.bool,
|
||||
onHide: PropTypes.func
|
||||
}
|
||||
|
||||
export default AddManagerModal
|
@ -0,0 +1,34 @@
|
||||
.add-ec-manager-modal {
|
||||
.ant-modal-body {
|
||||
padding-bottom: 0;
|
||||
|
||||
.ant-table-thead {
|
||||
background: #fafafa;
|
||||
}
|
||||
.ant-table-scroll {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.add-ec-manager-search {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 0;
|
||||
|
||||
&-label > label {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
height: 20px;
|
||||
margin-top: -20px;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
.ant-modal-footer {
|
||||
text-align: center;
|
||||
border-top: unset;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Tag, message } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
class MajorManager extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
schoolId: props.schoolId,
|
||||
majorId: props.majorId,
|
||||
canManage: props.canManage,
|
||||
managers: props.managers
|
||||
}
|
||||
|
||||
this.deleteManager = this.deleteManager.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if(this.props.managers.length !== prevProps.managers.length){
|
||||
this.setState({ managers: this.props.managers });
|
||||
}
|
||||
}
|
||||
|
||||
deleteManager(managerId){
|
||||
axios.delete(`/ec_major_schools/${this.state.majorId}/major_managers/${managerId}.json`).then(result => {
|
||||
if(result.status === 200){
|
||||
message.success('操作成功');
|
||||
}
|
||||
}).catch(e => { console.log(e) })
|
||||
}
|
||||
|
||||
render() {
|
||||
let { canManage, managers } = this.state;
|
||||
|
||||
return (
|
||||
<div className="manager-box-content">
|
||||
{
|
||||
managers && managers.map(manager => {
|
||||
return (
|
||||
<Tag key={manager.id} closable={canManage} onClose={() => { this.deleteManager(manager.id) }} color="blue">
|
||||
{ manager.name }
|
||||
</Tag>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
MajorManager.propTypes = {
|
||||
schoolId: PropTypes.string,
|
||||
majorId: PropTypes.number,
|
||||
canManage: PropTypes.bool,
|
||||
managers: PropTypes.array
|
||||
}
|
||||
|
||||
export default MajorManager
|
@ -0,0 +1,127 @@
|
||||
.ec-home {
|
||||
.head-image {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
background-size: 100% 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: -webkit-flex;
|
||||
}
|
||||
|
||||
.ec-home-item {
|
||||
background: #fff;
|
||||
|
||||
&-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&-label {
|
||||
margin-right: 20px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
&-tip {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&.major-list-item {
|
||||
.ec-home-item {
|
||||
&-head {
|
||||
margin-bottom: -24px;
|
||||
padding: 20px 30px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-tip {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.school-manager-item {
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
.ec-school-manager {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&-item {
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-name {
|
||||
display: block;
|
||||
text-align: center;
|
||||
max-width: 48px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.major-list-item {
|
||||
.major-list {
|
||||
&-container {
|
||||
|
||||
}
|
||||
|
||||
&-head {
|
||||
margin-top: -24px;
|
||||
padding: 20px 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.total { font-size: 12px; }
|
||||
}
|
||||
|
||||
&-body {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
&-row {
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
&:last-child { border-bottom: unset; }
|
||||
|
||||
&.head {
|
||||
background: #F5F5F5;
|
||||
}
|
||||
.ant-btn-link {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.operate-box {
|
||||
.link {
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
.manager-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #007bff;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue